diff --git a/kpdf/document.cpp b/kpdf/document.cpp index 5dc649b40..ec30ac0e9 100644 --- a/kpdf/document.cpp +++ b/kpdf/document.cpp @@ -1,236 +1,273 @@ /*************************************************************************** * Copyright (C) 2004 by Enrico Ros * * * * 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 includes #include #include #include // local includes #include "PDFDoc.h" //#include "TextOutputDev.h" #include "kpdf_error.h" #include "document.h" #include "page.h" +/* Notes: +- FIXME event queuing to avoid flow interruption (!!??) maybe avoided by the + warning to not call something 'active' inside an observer method. +- TODO implement filtering (on: "incremental search", "annotated pages", +*/ + // structure used internally by KPDFDocument for data storage -struct KPDFDocumentPrivate +class KPDFDocumentPrivate { +public: // document related QMutex docLock; PDFDoc * pdfdoc; int currentPage; QValueVector< KPDFPage* > pages; // observers related (note: won't delete oservers) QValueList< KPDFDocumentObserver* > observers; }; #define foreachObserver( cmd ) {\ QValueList::iterator it = d->observers.begin();\ QValueList::iterator end = d->observers.end();\ for ( ; it != end ; ++ it ) { (*it)-> cmd ; } } /* * KPDFDocument class */ KPDFDocument::KPDFDocument() { d = new KPDFDocumentPrivate; d->pdfdoc = 0; + d->currentPage = -1; } KPDFDocument::~KPDFDocument() { close(); delete d; } bool KPDFDocument::openFile( const QString & docFile ) { // docFile is always local so we can use QFile on it QFile fileReadTest( docFile ); if ( !fileReadTest.open( IO_ReadOnly ) ) return false; fileReadTest.close(); GString *filename = new GString( QFile::encodeName( docFile ) ); delete d->pdfdoc; d->pdfdoc = new PDFDoc( filename, 0, 0 ); deletePages(); if ( !d->pdfdoc->isOk() || d->pdfdoc->getNumPages() < 1 ) { delete d->pdfdoc; d->pdfdoc = 0; return false; } // clear xpdf errors errors::clear(); // build Pages + d->currentPage = -1; uint pageCount = d->pdfdoc->getNumPages(); d->pages.resize( pageCount ); for ( uint i = 0; i < pageCount ; i++ ) - d->pages[i] = new KPDFPage( i, d->pdfdoc->getPageHeight(i + 1), d->pdfdoc->getPageWidth(i + 1) ); + d->pages[i] = new KPDFPage( i, d->pdfdoc->getPageWidth(i+1), d->pdfdoc->getPageHeight(i+1) ); //filter = NONE; TODO sendFilteredPageList(); - setCurrentPage( 0 ); + slotSetCurrentPage( 0 ); return true; } void KPDFDocument::close() { //stopRunningJobs()... deletePages(); delete d->pdfdoc; d->pdfdoc = 0; } -const KPDFPage * KPDFDocument::page( uint n ) const + +uint KPDFDocument::currentPage() const { - return ( n < d->pages.count() ) ? d->pages[n] : 0; + return d->currentPage; } uint KPDFDocument::pages() const { return d->pdfdoc ? d->pdfdoc->getNumPages() : 0; } -void KPDFDocument::setCurrentPage( uint page, float position ) -{ //FIXME event queuing to avoid flow interruption (!!??) - if ( (int)page == d->currentPage ) //TODO check position +bool KPDFDocument::atBegin() const +{ + return d->currentPage < 1; +} + +bool KPDFDocument::atEnd() const +{ + return d->currentPage >= ((int)d->pages.count() - 1); +} + +const KPDFPage * KPDFDocument::page( uint n ) const +{ + return ( n < d->pages.count() ) ? d->pages[n] : 0; +} + + +void KPDFDocument::slotSetCurrentPage( int page ) +{ + slotSetCurrentPagePosition( page, 0.0 ); +} + +void KPDFDocument::slotSetCurrentPagePosition( int page, float position ) +{ + if ( page == d->currentPage ) //TODO check position return; d->currentPage = page; foreachObserver( pageSetCurrent( page, position ) ); pageChanged(); } -void KPDFDocument::find( bool /*nextMatch*/, const QString & /*text*/ ) +void KPDFDocument::slotFind( bool /*nextMatch*/, const QString & /*text*/ ) { /* TextOutputDev *textOut; Unicode *u; bool found; double xMin1, yMin1, xMax1, yMax1; int len, pg; // This is more or less copied from what xpdf does, surely can be optimized len = s.length(); u = (Unicode *)gmalloc(len * sizeof(Unicode)); for (int i = 0; i < len; ++i) u[i] = (Unicode)(s.latin1()[i] & 0xff); // search current found = m_outputDev->find(u, len, next); if (!found) { // search following pages textOut = new TextOutputDev(NULL, gTrue, gFalse, gFalse); if (!textOut->isOk()) { gfree(u); delete textOut; return; } pg = m_currentPage + 1; while(!found && pg <= d->pdfdoc->getNumPages()) { m_docMutex.lock(); d->pdfdoc->displayPage(textOut, pg, 72, 72, 0, gTrue, gFalse); m_docMutex.unlock(); found = textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse, &xMin1, &yMin1, &xMax1, &yMax1); if (!found) pg++; } if (!found && m_currentPage != 1) { if (KMessageBox::questionYesNo(widget(), i18n("End of document reached.\nContinue from the beginning?")) == KMessageBox::Yes) { // search previous pages pg = 1; while(!found && pg < m_currentPage) { m_docMutex.lock(); d->pdfdoc->displayPage(textOut, pg, 72, 72, 0, gTrue, gFalse); m_docMutex.unlock(); found = textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse, &xMin1, &yMin1, &xMax1, &yMax1); if (!found) pg++; } } } delete textOut; if (found) { kdDebug() << "found at " << pg << endl; goToPage(pg); // xpdf says: can happen that we do not find the text if coalescing is bad OUCH m_outputDev->find(u, len, false); } else { if (next) KMessageBox::information(widget(), i18n("No more matches found for '%1'.").arg(s)); else KMessageBox::information(widget(), i18n("No matches found for '%1'.").arg(s)); } } if (found) m_findText = s; else m_findText = QString::null; gfree(u); */ } -void KPDFDocument::goToLink( /* QString anchor */ ) +void KPDFDocument::slotGoToLink( /* QString anchor */ ) { +} +void KPDFDocument::slotSetZoom( float /*zoom*/ ) +{ +} + +void KPDFDocument::slotChangeZoom( float /*offset*/ ) +{ } void KPDFDocument::addObserver( KPDFDocumentObserver * pObserver ) { d->observers.push_back( pObserver ); } void KPDFDocument::sendFilteredPageList() -{ //TODO implement filtering - +{ // make up a value list of the pages [1,2,3..] uint pageCount = d->pages.count(); QValueList pagesList; for ( uint i = 0; i < pageCount ; i++ ) pagesList.push_back( i ); // send the list to observers foreachObserver( pageSetup( pagesList ) ); } void KPDFDocument::deletePages() { if ( d->pages.isEmpty() ) return; // broadcast an empty page list to observers QValueList pagesList; foreachObserver( pageSetup( pagesList ) ); // delete pages and clear container for ( uint i = 0; i < d->pages.count() ; i++ ) delete d->pages[i]; d->pages.clear(); d->currentPage = -1; } #include "document.moc" diff --git a/kpdf/document.h b/kpdf/document.h index 2af3232f2..2cbd22e03 100644 --- a/kpdf/document.h +++ b/kpdf/document.h @@ -1,84 +1,89 @@ /*************************************************************************** * Copyright (C) 2004 by Enrico Ros * * * * 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_DOCUMENT_H_ #define _KPDF_DOCUMENT_H_ #include #include class KPDFPage; /** * @short Base class for objects being notified when something changes. * * Inherit this class and call KPDFDocument->addObserver( obsClass ) to get * notified of asyncronous events (a new thumbnail has arrived, a pixmap has * changed, and other events). */ class KPDFDocumentObserver { public: // monitor changes in pixmaps (generation thread complete) virtual void notifyThumbnailChanged( int /*pageNumber*/ ) {}; virtual void notifyPixmapChanged( int /*pageNumber*/ ) {}; // commands from the Document to observers virtual void pageSetup( const QValueList & /*pages*/ ) {}; virtual void pageSetCurrent( int /*pageNumber*/, float /*position*/ ) {}; virtual void pageSetHilight( int /*x*/, int /*y*/, int /*width*/, int /*height*/ ) {}; }; /** - * @short The information container. Actions (like find) take place here. + * @short The information container. Actions (like open,find) take place here. * * xxxxxx * yyy. */ class KPDFDocument : public QObject { Q_OBJECT public: KPDFDocument(); ~KPDFDocument(); // observers related methods void addObserver( KPDFDocumentObserver * pObserver ); // document handling bool openFile( const QString & docFile ); void close(); - // document control - const KPDFPage * page( uint page ) const; + // document query + uint currentPage() const; uint pages() const; - uint currentPage() const { return 0; } - void setCurrentPage( uint page, float position = 0.0 ); - void find( bool nextMatch, const QString & text = "" ); - void goToLink( /* QString anchor */ ); - bool atBegin() {return false;} - bool atEnd() {return false;} + bool atBegin() const; + bool atEnd() const; + const KPDFPage * page( uint page ) const; - void setZoom( float zoom ) {}; - void zoom( float offset ) {}; +public slots: + // document commands via slots + void slotSetCurrentPage( int page ); + void slotSetCurrentPagePosition( int page, float position ); + void slotFind( bool nextMatch, const QString & text = "" ); + void slotGoToLink( /* QString anchor */ ); + void slotSetZoom( float zoom ); + void slotChangeZoom( float offset ); signals: + // notify changes via signals void pageChanged(); + void zoomChanged( float newZoom ); private: void sendFilteredPageList(); void deletePages(); class KPDFDocumentPrivate * d; }; #endif diff --git a/kpdf/kpdf_pagewidget.cc b/kpdf/kpdf_pagewidget.cc index 8273414bf..1ee7a4c62 100644 --- a/kpdf/kpdf_pagewidget.cc +++ b/kpdf/kpdf_pagewidget.cc @@ -1,401 +1,455 @@ /*************************************************************************** * Copyright (C) 2002 by Wilco Greven * * Copyright (C) 2003 by Christophe Devriese * * * * Copyright (C) 2003 by Laurent Montel * * Copyright (C) 2003 by Dirk Mueller * * Copyright (C) 2004 by Albert Astals Cid * * Copyright (C) 2004 by James Ots * * * * 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 #include #include #include #include #include "PDFDoc.h" #include "kpdf_pagewidget.h" #include "kpdf_pagewidget.moc" #include "QOutputDev.h" #include #include namespace KPDF { - PageWidget::PageWidget(QWidget* parent, const char* name, QMutex *docMutex) - : QScrollView(parent, name, WRepaintNoErase), + PageWidget::PageWidget(QWidget *parent, KPDFDocument *doc) + : QScrollView(parent, "pageWidget", WRepaintNoErase), m_doc(0), - m_docMutex(docMutex), m_zoomFactor( 1.0 ), + m_document( doc ), m_currentPage( 1 ), m_pressedAction( 0 ), m_selection( false ) { SplashColor paperColor; paperColor.rgb8 = splashMakeRGB8(0xff, 0xff, 0xff); m_outputdev = new QOutputDev(paperColor); setFocusPolicy( QWidget::StrongFocus ); setAcceptDrops( true ); viewport()->setFocusPolicy( QWidget::WheelFocus ); QPushButton * w = new QPushButton( viewport() ); w->setPixmap( SmallIcon("up") ); setCornerWidget( w ); } PageWidget::~PageWidget() { delete m_outputdev; } void PageWidget::setPDFDocument(PDFDoc* doc) { m_doc = doc; m_outputdev -> startDoc(doc->getXRef()); m_currentPage = 1; updatePixmap(); } void PageWidget::setPixelsPerPoint(float ppp) { m_ppp = ppp; } void PageWidget::contentsMousePressEvent(QMouseEvent* e) { if (m_doc == 0) return; if ( e->button() & LeftButton ) { m_dragGrabPos = e -> globalPos(); setCursor( sizeAllCursor ); } else if ( e->button() & RightButton ) { emit rightClick(); } m_pressedAction = m_doc->findLink(e->x()/m_ppp, e->y()/m_ppp); } void PageWidget::contentsMouseReleaseEvent(QMouseEvent* e) { if (m_doc == 0) return; if ( e -> button() & LeftButton ) { setCursor( arrowCursor ); } else { LinkAction* action = m_doc->findLink(e->x()/m_ppp, e->y()/m_ppp); if (action == m_pressedAction) emit linkClicked(action); m_pressedAction = 0; } } void PageWidget::contentsMouseMoveEvent(QMouseEvent* e) { if (m_doc == 0) return; if ( e->state() & LeftButton ) { QPoint delta = m_dragGrabPos - e->globalPos(); scrollBy( delta.x(), delta.y() ); m_dragGrabPos = e->globalPos(); } else { LinkAction* action = m_doc->findLink(e->x()/m_ppp, e->y()/m_ppp); setCursor(action != 0 ? Qt::PointingHandCursor : Qt::ArrowCursor); } } void PageWidget::drawContents ( QPainter *p, int clipx, int clipy, int clipw, int cliph ) { QImage im; QColor bc(KGlobalSettings::calculateAlternateBackgroundColor(KGlobalSettings::baseColor())); if (m_outputdev) im = m_outputdev->getImage(); if ( !im.isNull() ) { p->drawImage ( clipx, clipy, im, clipx, clipy, clipw, cliph ); if ((clipw + clipx) > im.width()) p->fillRect ( im.width(), clipy, clipw - (im.width() - clipx), im.height() - clipy, bc ); if ((cliph + clipy) > im.height()) p->fillRect ( clipx, im.height(), clipw, cliph - (im.height() - clipy), bc ); if (m_selection) { p->setBrush(Qt::SolidPattern); p->setPen(QPen(Qt::black, 1)); // should not be necessary bug a Qt bug makes it necessary p->setRasterOp(Qt::NotROP); p->drawRect(qRound(m_xMin*m_zoomFactor), qRound(m_yMin*m_zoomFactor), qRound((m_xMax- m_xMin)*m_zoomFactor), qRound((m_yMax- m_yMin)*m_zoomFactor)); } } else p->fillRect ( clipx, clipy, clipw, cliph, bc ); } void PageWidget::zoomTo( double _value ) { if ( m_zoomFactor != _value) { m_zoomFactor = _value; updatePixmap(); } } void PageWidget::zoomIn() { m_zoomFactor += 0.1; updatePixmap(); } void PageWidget::zoomOut() { if ( (m_zoomFactor-0.1)<0.1 ) return; m_zoomFactor -= 0.1; updatePixmap(); } void PageWidget::setPage(int page) { // any idea why this mutex is necessary? static QMutex mutex; m_selection = false; Q_ASSERT(mutex.locked() == false); mutex.lock(); if (m_doc) { m_currentPage = page; } else { m_currentPage = 0; } updatePixmap(); mutex.unlock(); } void PageWidget::enableScrollBars( bool b ) { setHScrollBarMode( b ? Auto : AlwaysOff ); setVScrollBarMode( b ? Auto : AlwaysOff ); } void PageWidget::scrollRight() { horizontalScrollBar()->addLine(); } void PageWidget::scrollLeft() { horizontalScrollBar()->subtractLine(); } void PageWidget::scrollDown() { verticalScrollBar()->addLine(); } void PageWidget::scrollUp() { verticalScrollBar()->subtractLine(); } void PageWidget::scrollBottom() { verticalScrollBar()->setValue( verticalScrollBar()->maxValue() ); } void PageWidget::scrollTop() { verticalScrollBar()->setValue( verticalScrollBar()->minValue() ); } void PageWidget::keyPressEvent( QKeyEvent* e ) { switch ( e->key() ) { case Key_Up: if ( atTop() ) emit ReadUp(); else scrollUp(); break; case Key_Down: if ( atBottom() ) emit ReadDown(); else scrollDown(); break; case Key_Left: scrollLeft(); break; case Key_Right: scrollRight(); break; case Key_Space: { if( e->state() != ShiftButton ) { emit spacePressed(); } } default: e->ignore(); return; } e->accept(); } bool PageWidget::atTop() const { return verticalScrollBar()->value() == verticalScrollBar()->minValue(); } bool PageWidget::atBottom() const { return verticalScrollBar()->value() == verticalScrollBar()->maxValue(); } void PageWidget::wheelEvent( QWheelEvent *e ) { int delta = e->delta(); e->accept(); if ((e->state() & ControlButton) == ControlButton) { if ( e->delta() > 0 ) emit ZoomOut(); else emit ZoomIn(); } else if ( delta <= -120 && atBottom() ) { emit ReadDown(); } else if ( delta >= 120 && atTop()) { emit ReadUp(); } else QScrollView::wheelEvent( e ); } void PageWidget::updatePixmap() { if ( m_doc ) { // Pixels per point when the zoomFactor is 1. const float ppp = (float)QPaintDevice::x11AppDpiX() * m_zoomFactor; // pixels per point - m_docMutex->lock(); - m_doc->displayPage(m_outputdev, m_currentPage, ppp, ppp, 0, true, true); - m_docMutex->unlock(); + //m_docMutex->lock(); + //m_doc->displayPage(m_outputdev, m_currentPage, ppp, ppp, 0, true, true); + //m_docMutex->unlock(); resizeContents ( m_outputdev->getImage().width ( ), m_outputdev->getImage().height ( )); viewport()->update(); } } bool PageWidget::readUp() { if( atTop() ) return false; else { int newValue = QMAX( verticalScrollBar()->value() - height() + 50, verticalScrollBar()->minValue() ); /* int step = 10; int value = verticalScrollBar()->value(); while( value > newValue - step ) { verticalScrollBar()->setValue( value ); value -= step; } */ verticalScrollBar()->setValue( newValue ); return true; } } void PageWidget::dropEvent( QDropEvent* ev ) { KURL::List lst; if ( KURLDrag::decode( ev, lst ) ) { emit urlDropped( lst.first() ); } } void PageWidget::dragEnterEvent( QDragEnterEvent * ev ) { ev->accept(); } bool PageWidget::readDown() { if( atBottom() ) return false; else { int newValue = QMIN( verticalScrollBar()->value() + height() - 50, verticalScrollBar()->maxValue() ); /* int step = 10; int value = verticalScrollBar()->value(); while( value < newValue + step ) { verticalScrollBar()->setValue( value ); value += step; } */ verticalScrollBar()->setValue( newValue ); return true; } } bool PageWidget::find(Unicode *u, int len, bool next) { bool b; if (!next) { // ensure we are searching the whole page m_xMin = 0; m_yMin = 0; } b = m_outputdev -> find(u, len, !next, true, next, false, &m_xMin, &m_yMin, &m_xMax, &m_yMax); m_xMin = m_xMin / m_zoomFactor; m_yMin = m_yMin / m_zoomFactor; m_xMax = m_xMax / m_zoomFactor; m_yMax = m_yMax / m_zoomFactor; m_selection = b; updateContents(); return b; } } +/** TO BE IMPORTED +void Part::displayPage( int pageNumber ) +{ + if (pageNumber <= 0 || pageNumber > m_doc->getNumPages()) + return; + updateActions(); + const double pageWidth = m_doc->getPageWidth (pageNumber) * m_zoomFactor; + const double pageHeight = m_doc->getPageHeight(pageNumber) * m_zoomFactor; + + // Pixels per point when the zoomFactor is 1. + const float basePpp = QPaintDevice::x11AppDpiX() / 72.0; + + switch (m_zoomMode) + { + case FitWidth: + { + const double pageAR = pageWidth/pageHeight; // Aspect ratio + + const int canvasWidth = m_pageWidget->contentsRect().width(); + const int canvasHeight = m_pageWidget->contentsRect().height(); + const int scrollBarWidth = m_pageWidget->verticalScrollBar()->width(); + + // Calculate the height so that the page fits the viewport width + // assuming that we need a vertical scrollbar. + float height = float(canvasWidth - scrollBarWidth) / pageAR; + + // If the vertical scrollbar wasn't needed after all, calculate the page + // size so that the page fits the viewport width without the scrollbar. + if (ceil(height) <= canvasHeight) + { + height = float(canvasWidth) / pageAR; + + // Handle the rare case that enlarging the page resulted in the need of + // a vertical scrollbar. We can fit the page to the viewport height in + // this case. + if (ceil(height) > canvasHeight) + height = float(canvasHeight) * pageAR; + } + + m_zoomFactor = (height / pageHeight) / basePpp; + break; + } + case FixedFactor: + default: + break; + } + +//const float ppp = basePpp * m_zoomFactor; // pixels per point +// m_doc->displayPage(m_pageWidget, pageNumber, int(m_zoomFactor * ppp * 72.0), 0, true); +// m_pageWidget->show(); +// m_currentPage = pageNumber; +} +*/ + // vim:ts=2:sw=2:tw=78:et diff --git a/kpdf/kpdf_pagewidget.h b/kpdf/kpdf_pagewidget.h index 2d4672ff3..8ba0be0d9 100644 --- a/kpdf/kpdf_pagewidget.h +++ b/kpdf/kpdf_pagewidget.h @@ -1,158 +1,158 @@ /*************************************************************************** * Copyright (C) 2002 by Wilco Greven * * Copyright (C) 2003 by Christophe Devriese * * * * Copyright (C) 2003 by Laurent Montel * * Copyright (C) 2003 by Kurt Pfeifle * * Copyright (C) 2004 by Albert Astals Cid * * * * 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_PAGEWIDGET_H_ #define _KPDF_PAGEWIDGET_H_ #include #include "document.h" #include "CharTypes.h" class KURL; -class QMutex; class QWidget; class LinkAction; class PDFDoc; class QOutputDev; +class KPDFDocument; namespace KPDF { /** * Widget displaying a pixmap containing a PDF page and Links. */ class PageWidget : public QScrollView, public KPDFDocumentObserver { Q_OBJECT enum ZoomMode { FitInWindow, FitWidth, FitVisible, FixedFactor }; public: - PageWidget(QWidget* parent, const char* name, QMutex *docMutex); + PageWidget(QWidget *parent, KPDFDocument *document); ~PageWidget(); void setPDFDocument(PDFDoc*); void setPixelsPerPoint(float); /* void setLinks(); */ // inherited from KPDFDocumentObserver void pageSetup( const QValueList & /*pages*/ ) {/* m_outputDev->setPDFDocument(d->pdfdoc); */} void pageSetCurrent( int /*pageNumber*/, float /*position*/ ) { //m_pageWidget->setPage(m_currentPage); } void setPage(int pagenum); void enableScrollBars( bool b ); /** * Return true if the top resp. bottom of the page is visible. */ bool atTop() const; bool atBottom() const; void zoomTo( double _value ); bool find(Unicode *u, int len, bool next); public slots: void zoomIn(); void zoomOut(); void updatePixmap(); void scrollUp(); void scrollDown(); void scrollRight(); void scrollLeft(); void scrollBottom(); void scrollTop(); bool readUp(); bool readDown(); signals: void linkClicked(LinkAction*); void ReadUp(); void ReadDown(); void ZoomOut(); void ZoomIn(); void rightClick(); void urlDropped( const KURL& ); void spacePressed(); protected: virtual void keyPressEvent( QKeyEvent* ); void contentsMousePressEvent(QMouseEvent*); void contentsMouseReleaseEvent(QMouseEvent*); void contentsMouseMoveEvent(QMouseEvent*); virtual void wheelEvent( QWheelEvent * ); virtual void drawContents ( QPainter *p, int, int, int, int ); virtual void dragEnterEvent( QDragEnterEvent* ); virtual void dropEvent( QDropEvent* ); private: QOutputDev *m_outputdev; PDFDoc* m_doc; - QMutex* m_docMutex; float m_ppp; // Pixels per point float m_zoomFactor; ZoomMode m_zoomMode; + KPDFDocument *m_document; // first page is page 1 int m_currentPage; QPoint m_dragGrabPos; LinkAction* m_pressedAction; bool m_selection; double m_xMin, m_yMin, m_xMax, m_yMax; }; } /* ZoomMode m_zoomMode; float m_zoomFactor; // Do with these first. We can always add the other zoommodes which can // be specified in a Destination later. enum ZoomMode { FitInWindow, FitWidth, FitVisible, FixedFactor }; */ /* connect( m_pageWidget, SIGNAL( ReadUp() ), SLOT( slotReadUp() )); connect( m_pageWidget, SIGNAL( ReadDown() ), SLOT( slotReadDown() )); connect( m_pageWidget, SIGNAL( spacePressed() ), this, SLOT( slotReadDown() ) ); void Part::slotReadUp() { if( !m_doc ) return; if( !m_pageWidget->readUp() ) { if ( previousPage() ) m_pageWidget->scrollBottom(); } } void Part::slotReadDown() { if( !m_doc ) return; if( !m_pageWidget->readDown() ) { if ( nextPage() ) m_pageWidget->scrollTop(); } } */ #endif // vim:ts=2:sw=2:tw=78:et diff --git a/kpdf/kpdf_part.cpp b/kpdf/kpdf_part.cpp index b8e8e8c17..364b75bc8 100644 --- a/kpdf/kpdf_part.cpp +++ b/kpdf/kpdf_part.cpp @@ -1,645 +1,613 @@ /*************************************************************************** * Copyright (C) 2002 by Wilco Greven * * Copyright (C) 2002 by Chris Cheney * * Copyright (C) 2002 by Malcolm Hunter * * Copyright (C) 2003-2004 by Christophe Devriese * * * * Copyright (C) 2003 by Daniel Molkentin * * Copyright (C) 2003 by Andy Goossens * * Copyright (C) 2003 by Dirk Mueller * * Copyright (C) 2003 by Laurent Montel * * Copyright (C) 2004 by Dominique Devriese * * Copyright (C) 2004 by Christoph Cullmann * * Copyright (C) 2004 by Henrique Pinto * * Copyright (C) 2004 by Waldo Bastian * * Copyright (C) 2004 by Albert Astals Cid * * Copyright (C) 2004 by Antti Markus * * * * 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 "kpdf_part.moc" #include -#include -#include -#include +#include #include -#include +#include +#include #include -#include #include #include #include #include #include #include -#include #include #include #include +#include +#include #include "kpdf_error.h" #include "GString.h" #include "GlobalParams.h" #include "QOutputDevKPrinter.h" #include "thumbnaillist.h" #include "kpdf_pagewidget.h" #include "document.h" typedef KParts::GenericFactory KPDFPartFactory; K_EXPORT_COMPONENT_FACTORY(libkpdfpart, KPDFPartFactory) using namespace KPDF; unsigned int Part::m_count = 0; Part::Part(QWidget *parentWidget, const char *widgetName, QObject *parent, const char *name, const QStringList & /*args*/ ) : KParts::ReadOnlyPart(parent, name) { // create browser extension (for printing when embedded into browser) new BrowserExtension(this); // xpdf 'extern' global class (m_count is a static instance counter) TODO check for wasted creation globalParams = new GlobalParams(""); m_count++; // we need an instance setInstance(KPDFPartFactory::instance()); + // build the document + document = new KPDFDocument(); + connect( document, SIGNAL( pageChanged() ), this, SLOT( updateActions() ) ); + // build widgets - QHBox *widget = new QHBox(parentWidget, widgetName); - widget->setSpacing(3); - widget->setMargin(3); + QSplitter *split = new QSplitter(parentWidget, widgetName); + split->setOpaqueResize( true ); - m_thumbnailList = new ThumbnailList(widget, &m_docMutex); - m_thumbnailList->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)1, (QSizePolicy::SizeType)7, 0, 0, m_thumbnailList->sizePolicy().hasHeightForWidth() ) ); - m_thumbnailList->setMaximumSize( QSize( 75, 32767 ) ); - m_thumbnailList->setColumnWidth(0, 75); + m_thumbnailList = new ThumbnailList(split, document); + m_thumbnailList->setMaximumWidth( 90 ); + m_thumbnailList->setMinimumWidth( 50 ); + document->addObserver( m_thumbnailList ); - m_pageWidget = new KPDF::PageWidget( widget, "outputdev", &m_docMutex ); + m_pageWidget = new KPDF::PageWidget(split, document); connect( m_pageWidget, SIGNAL( urlDropped( const KURL& ) ), SLOT( openURL( const KURL & ))); //connect(m _pageWidget, SIGNAL( rightClick() ), this, SIGNAL( rightClick() )); - - // FIXME is the following line really necessary?? - widget->resize( QSize(623, 381).expandedTo(widget->minimumSizeHint()) ); - setWidget(widget); - - // build the document - document = new KPDFDocument(); - connect( document, SIGNAL( pageChanged() ), this, SLOT( updateActions() ) ); - document->addObserver( m_thumbnailList ); document->addObserver( m_pageWidget ); + //TODO split->setSizes( QValueList list ); loaded from config file (last setup) + setWidget(split); + // ACTIONS KActionCollection * ac = actionCollection(); // Page Traversal actions m_gotoPage = KStdAction::gotoPage( this, SLOT( slotGoToPage() ), ac, "goToPage" ); m_prevPage = KStdAction::prior(this, SLOT(slotPreviousPage()), ac, "previous_page"); m_prevPage->setWhatsThis( i18n( "Moves to the previous page of the document" ) ); m_nextPage = KStdAction::next(this, SLOT(slotNextPage()), ac, "next_page" ); m_nextPage->setWhatsThis( i18n( "Moves to the next page of the document" ) ); m_firstPage = KStdAction::firstPage( this, SLOT( slotGotoStart() ), ac, "goToStart" ); m_firstPage->setWhatsThis( i18n( "Moves to the first page of the document" ) ); m_lastPage = KStdAction::lastPage( this, SLOT( slotGotoEnd() ), ac, "goToEnd" ); m_lastPage->setWhatsThis( i18n( "Moves to the last page of the document" ) ); // Find actions m_find = KStdAction::find(this, SLOT(slotFind()), ac, "find"); m_find->setEnabled(false); m_findNext = KStdAction::findNext(this, SLOT(slotFindNext()), ac, "find_next"); m_findNext->setEnabled(false); // Zoom actions const double zoomValue[14] = {0.125,0.25,0.3333,0.5,0.6667,0.75,1,1.25,1.50,2,3,4,6,8 }; m_zoomTo = new KSelectAction( i18n( "Zoom" ), "zoomTo", 0, ac, "zoomTo" ); connect( m_zoomTo, SIGNAL( activated( const QString & ) ), this, SLOT( slotZoom( const QString& ) ) ); m_zoomTo->setEditable( true ); m_zoomTo->clear(); QStringList translated; QString localValue; QString double_oh("00"); int idx = 0; int cur = 0; for ( int i = 0; i < 10;i++) { localValue = KGlobal::locale()->formatNumber( zoomValue[i] * 100.0, 2 ); localValue.remove( KGlobal::locale()->decimalSymbol()+double_oh ); translated << QString( "%1%" ).arg( localValue ); if ( zoomValue[i] == 1.0 ) idx = cur; ++cur; } m_zoomTo->setItems( translated ); m_zoomTo->setCurrentItem( idx ); KStdAction::zoomIn( m_pageWidget, SLOT(zoomIn()), ac, "zoom_in" ); KStdAction::zoomOut( m_pageWidget, SLOT(zoomOut()), ac, "zoom_out" ); m_fitToWidth = new KToggleAction( i18n("Fit to Page &Width"), 0, ac, "fit_to_width" ); connect( m_fitToWidth, SIGNAL( toggled( bool ) ), SLOT( slotFitToWidthToggled( bool ) ) ); // other actions (printing, show/hide stuff, saving) KStdAction::printPreview( this, SLOT( slotPrintPreview() ), ac ); m_showScrollBars = new KToggleAction( i18n( "Show &Scrollbars" ), 0, ac, "show_scrollbars" ); m_showScrollBars->setCheckedState(i18n("Hide &Scrollbars")); connect( m_showScrollBars, SIGNAL( toggled( bool ) ), SLOT( slotToggleScrollBars( bool ) ) ); m_showPageList = new KToggleAction( i18n( "Show &Page List" ), 0, ac, "show_page_list" ); m_showPageList->setCheckedState(i18n("Hide &Page List")); connect( m_showPageList, SIGNAL( toggled( bool ) ), SLOT( slotToggleThumbnails( bool ) ) ); KStdAction::saveAs(this, SLOT(slotSaveFileAs()), ac, "save"); // set our XML-UI resource file setXMLFile("kpdf_part.rc"); readSettings(); updateActions(); } -// ### - Part::~Part() { delete document; writeSettings(); if ( --m_count == 0 ) delete globalParams; } -void Part::slotZoom( const QString & nz ) -{ - QString z = nz; - z.remove( z.find( '%' ), 1 ); - bool isNumber = true; - double zoom = KGlobal::locale()->readNumber( z, &isNumber ) / 100; - - if ( isNumber ) - document->setZoom( zoom ); -} - -void Part::slotZoomIn() +KAboutData* Part::createAboutData() { - document->zoom( 0.1 ); + // the non-i18n name here must be the same as the directory in + // which the part's rc file is installed ('partrcdir' in the + // Makefile) + KAboutData* aboutData = new KAboutData("kpdfpart", I18N_NOOP("KPDF::Part"), "0.1"); + aboutData->addAuthor("Wilco Greven", 0, "greven@kde.org"); + return aboutData; } -void Part::slotZoomOut() +bool Part::openFile() { - document->zoom( -0.1 ); + bool ok = document->openFile( m_file ); + m_find->setEnabled(ok); + m_findNext->setEnabled(ok); + return ok; } -void Part::slotGoToPage() +bool Part::closeURL() { - bool ok = false; - //TODO a better dialog with a slider too - int num = KInputDialog::getInteger(i18n("Go to Page"), i18n("Page:"), document->currentPage(), - 1, document->pages(), 1, 10, &ok/*, _part->widget()*/); - if (ok) - document->setCurrentPage( num ); + document->close(); + return KParts::ReadOnlyPart::closeURL(); } void Part::updateActions() { if ( document->pages() > 0 ) { - m_firstPage->setEnabled( !document->atBegin() ); - m_prevPage->setEnabled( !document->atBegin() ); - m_lastPage->setEnabled( !document->atEnd() ); - m_nextPage->setEnabled( !document->atEnd() ); + m_gotoPage->setEnabled(document->pages()>1); + m_firstPage->setEnabled(!document->atBegin()); + m_prevPage->setEnabled(!document->atBegin()); + m_lastPage->setEnabled(!document->atEnd()); + m_nextPage->setEnabled(!document->atEnd()); } else { + m_gotoPage->setEnabled(false); m_firstPage->setEnabled(false); m_lastPage->setEnabled(false); m_prevPage->setEnabled(false); m_nextPage->setEnabled(false); } } +void Part::readSettings() +{ + KConfigGroup general( KPDFPartFactory::instance()->config(), "General" ); + m_showScrollBars->setChecked( general.readBoolEntry( "ShowScrollBars", true ) ); + slotToggleScrollBars( m_showScrollBars->isChecked() ); + m_showPageList->setChecked( general.readBoolEntry( "ShowPageList", true ) ); + slotToggleThumbnails( m_showPageList->isChecked() ); +} + void Part::writeSettings() { KConfigGroup general( KPDFPartFactory::instance()->config(), "General" ); general.writeEntry( "ShowScrollBars", m_showScrollBars->isChecked() ); general.writeEntry( "ShowPageList", m_showPageList->isChecked() ); general.sync(); } -void Part::readSettings() +//BEGIN go to page dialog +class KPDFGotoPageDialog : public KDialogBase { - KConfigGroup general( KPDFPartFactory::instance()->config(), "General" ); - m_showScrollBars->setChecked( general.readBoolEntry( "ShowScrollBars", true ) ); - slotToggleScrollBars( m_showScrollBars->isChecked() ); - m_showPageList->setChecked( general.readBoolEntry( "ShowPageList", true ) ); - slotToggleThumbnails( m_showPageList->isChecked() ); +public: + KPDFGotoPageDialog(QWidget *p, int current, int max) : KDialogBase(p, 0L, true, i18n("Go to Page"), Ok | Cancel, Ok) { + QWidget *w = new QWidget(this); + setMainWidget(w); + + QVBoxLayout *topLayout = new QVBoxLayout( w, 0, spacingHint() ); + e1 = new KIntNumInput(current, w); + e1->setRange(1, max); + e1->setEditFocus(true); + + QLabel *label = new QLabel( e1,i18n("&Page:"), w ); + topLayout->addWidget(label); + topLayout->addWidget(e1); + topLayout->addSpacing(spacingHint()); // A little bit extra space + topLayout->addStretch(10); + e1->setFocus(); + } + + int getPage() { + return e1->value(); + } + + protected: + KIntNumInput *e1; +}; +//END go to page dialog + +void Part::slotGoToPage() +{ + KPDFGotoPageDialog pageDialog( m_pageWidget, document->currentPage() + 1, document->pages() ); + if ( pageDialog.exec() == QDialog::Accepted ) + document->slotSetCurrentPage( pageDialog.getPage() - 1 ); } -void Part::slotToggleScrollBars( bool show ) +void Part::slotPreviousPage() { - m_pageWidget->enableScrollBars( show ); + if ( !document->atBegin() ) + document->slotSetCurrentPage( document->currentPage() - 1 ); } -void Part::slotToggleThumbnails( bool show ) +void Part::slotNextPage() { - m_thumbnailList->setShown( show ); + if ( !document->atEnd() ) + document->slotSetCurrentPage( document->currentPage() + 1 ); +} + +void Part::slotGotoStart() +{ + document->slotSetCurrentPage( 0 ); } void Part::slotGotoEnd() { - document->setCurrentPage( document->pages() - 1 ); + document->slotSetCurrentPage( document->pages() - 1 ); } -void Part::slotGotoStart() +void Part::slotFind() { - document->setCurrentPage( 0 ); + KFindDialog dlg(widget()); + if (dlg.exec() == QDialog::Accepted) + document->slotFind( false, dlg.pattern() ); } -void Part::slotNextPage() +void Part::slotFindNext() { - if ( !document->atEnd() ) - document->setCurrentPage( document->currentPage() + 1 ); + document->slotFind( true ); } -void Part::slotPreviousPage() +void Part::slotZoom( const QString & nz ) { - if ( !document->atBegin() ) - document->setCurrentPage( document->currentPage() - 1 ); + QString z = nz; + z.remove( z.find( '%' ), 1 ); + bool isNumber = true; + double zoom = KGlobal::locale()->readNumber( z, &isNumber ) / 100; + + if ( isNumber ) + document->slotSetZoom( zoom ); } -KAboutData* Part::createAboutData() +void Part::slotZoomIn() { - // the non-i18n name here must be the same as the directory in - // which the part's rc file is installed ('partrcdir' in the - // Makefile) - KAboutData* aboutData = new KAboutData("kpdfpart", I18N_NOOP("KPDF::Part"), "0.1"); - aboutData->addAuthor("Wilco Greven", 0, "greven@kde.org"); - return aboutData; + document->slotChangeZoom( 0.1 ); } -bool Part::openFile() +void Part::slotZoomOut() { - bool ok = document->openFile( m_file ); - m_find->setEnabled(ok); - m_findNext->setEnabled(ok); - return ok; + document->slotChangeZoom( -0.1 ); } -bool Part::closeURL() +void Part::slotFitToWidthToggled( bool /*fit*/ ) { - document->close(); - return KParts::ReadOnlyPart::closeURL(); +/* + m_zoomMode = m_fitToWidth->isChecked() ? FitWidth : FixedFactor; + displayPage(m_currentPage); +*/ +} + +void Part::slotToggleScrollBars( bool show ) +{ + m_pageWidget->enableScrollBars( show ); +} + +void Part::slotToggleThumbnails( bool show ) +{ + m_thumbnailList->setShown( show ); } void Part::slotSaveFileAs() { KURL saveURL = KFileDialog::getSaveURL( url().isLocalFile() ? url().url() : url().fileName(), QString::null, widget(), QString::null ); if( !KIO::NetAccess::upload( url().path(), saveURL, static_cast( 0 ) ) ) ; // TODO: Proper error dialog } -void Part::displayPage( int /*pageNumber*/ ) -{ -/* - if (pageNumber <= 0 || pageNumber > m_doc->getNumPages()) - return; - updateActions(); - const double pageWidth = m_doc->getPageWidth (pageNumber) * m_zoomFactor; - const double pageHeight = m_doc->getPageHeight(pageNumber) * m_zoomFactor; - - // Pixels per point when the zoomFactor is 1. - const float basePpp = QPaintDevice::x11AppDpiX() / 72.0; - switch (m_zoomMode) - { - case FitWidth: - { - const double pageAR = pageWidth/pageHeight; // Aspect ratio - - const int canvasWidth = m_pageWidget->contentsRect().width(); - const int canvasHeight = m_pageWidget->contentsRect().height(); - const int scrollBarWidth = m_pageWidget->verticalScrollBar()->width(); - - // Calculate the height so that the page fits the viewport width - // assuming that we need a vertical scrollbar. - float height = float(canvasWidth - scrollBarWidth) / pageAR; - - // If the vertical scrollbar wasn't needed after all, calculate the page - // size so that the page fits the viewport width without the scrollbar. - if (ceil(height) <= canvasHeight) - { - height = float(canvasWidth) / pageAR; - - // Handle the rare case that enlarging the page resulted in the need of - // a vertical scrollbar. We can fit the page to the viewport height in - // this case. - if (ceil(height) > canvasHeight) - height = float(canvasHeight) * pageAR; - } - - m_zoomFactor = (height / pageHeight) / basePpp; - break; - } - case FixedFactor: - default: - break; - } -*/ -//const float ppp = basePpp * m_zoomFactor; // pixels per point -// m_doc->displayPage(m_pageWidget, pageNumber, int(m_zoomFactor * ppp * 72.0), 0, true); -// m_pageWidget->show(); -// m_currentPage = pageNumber; -} /* void Part::displayDestination(LinkDest* dest) { int pageNumber; // int dx, dy; if (dest->isPageRef()) { Ref pageRef = dest->getPageRef(); pageNumber = m_doc->findPage(pageRef.num, pageRef.gen); } else { pageNumber = dest->getPageNum(); } if (pageNumber <= 0 || pageNumber > m_doc->getNumPages()) { pageNumber = 1; } displayPage(pageNumber); return; if (fullScreen) { return; } switch (dest->getKind()) { case destXYZ: out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy); if (dest->getChangeLeft() || dest->getChangeTop()) { if (dest->getChangeLeft()) { hScrollbar->setPos(dx, canvas->getWidth()); } if (dest->getChangeTop()) { vScrollbar->setPos(dy, canvas->getHeight()); } canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos()); } //~ what is the zoom parameter? break; case destFit: case destFitB: //~ do fit hScrollbar->setPos(0, canvas->getWidth()); vScrollbar->setPos(0, canvas->getHeight()); canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos()); break; case destFitH: case destFitBH: //~ do fit out->cvtUserToDev(0, dest->getTop(), &dx, &dy); hScrollbar->setPos(0, canvas->getWidth()); vScrollbar->setPos(dy, canvas->getHeight()); canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos()); break; case destFitV: case destFitBV: //~ do fit out->cvtUserToDev(dest->getLeft(), 0, &dx, &dy); hScrollbar->setPos(dx, canvas->getWidth()); vScrollbar->setPos(0, canvas->getHeight()); canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos()); break; case destFitR: //~ do fit out->cvtUserToDev(dest->getLeft(), dest->getTop(), &dx, &dy); hScrollbar->setPos(dx, canvas->getWidth()); vScrollbar->setPos(dy, canvas->getHeight()); canvas->scroll(hScrollbar->getPos(), vScrollbar->getPos()); break; } }*/ /* void Part::executeAction(LinkAction* action) { if (action == 0) return; LinkActionKind kind = action->getKind(); switch (kind) { case actionGoTo: case actionGoToR: { LinkDest* dest = 0; GString* namedDest = 0; if (kind == actionGoTo) { if ((dest = ((LinkGoTo*)action)->getDest())) dest = dest->copy(); else if ((namedDest = ((LinkGoTo*)action)->getNamedDest())) namedDest = namedDest->copy(); } else { if ((dest = ((LinkGoToR*)action)->getDest())) dest = dest->copy(); else if ((namedDest = ((LinkGoToR*)action)->getNamedDest())) namedDest = namedDest->copy(); s = ((LinkGoToR*)action)->getFileName()->getCString(); //~ translate path name for VMS (deal with '/') if (!loadFile(fileName)) { delete dest; delete namedDest; return; } } if (namedDest != 0) { dest = m_doc->findDest(namedDest); delete namedDest; } if (dest != 0) { displayDestination(dest); delete dest; } else { if (kind == actionGoToR) displayPage(1); } break; } default: break; } }*/ -void Part::slotFitToWidthToggled( bool /*fit*/ ) -{ -/* - m_zoomMode = m_fitToWidth->isChecked() ? FitWidth : FixedFactor; - displayPage(m_currentPage); -*/ -} - - void Part::slotPrint() { /* if (m_doc == 0) return; double width, height; int landscape, portrait; KPrinter printer; printer.setPageSelection(KPrinter::ApplicationSide); printer.setMinMax(1, m_doc->getNumPages()); printer.setCurrentPage(m_currentPage); printer.setMargins(0, 0, 0, 0); // if some pages are landscape and others are not the most common win as kprinter does // not accept a per page setting landscape = 0; portrait = 0; for (int i = 1; i <= m_doc->getNumPages(); i++) { width = m_doc->getPageWidth(i); height = m_doc->getPageHeight(i); if (m_doc->getPageRotate(i) == 90 || m_doc->getPageRotate(i) == 270) qSwap(width, height); if (width > height) landscape++; else portrait++; } if (landscape > portrait) printer.setOrientation(KPrinter::Landscape); if (printer.setup(widget())) { doPrint( printer ); } */ } void Part::slotPrintPreview() { /* if (m_doc == 0) return; double width, height; int landscape, portrait; KPrinter printer; printer.setMinMax(1, m_doc->getNumPages()); printer.setPreviewOnly( true ); printer.setMargins(0, 0, 0, 0); // if some pages are landscape and others are not the most common win as kprinter does // not accept a per page setting landscape = 0; portrait = 0; for (int i = 1; i <= m_doc->getNumPages(); i++) { width = m_doc->getPageWidth(i); height = m_doc->getPageHeight(i); if (m_doc->getPageRotate(i) == 90 || m_doc->getPageRotate(i) == 270) qSwap(width, height); if (width > height) landscape++; else portrait++; } if (landscape > portrait) printer.setOption("orientation-requested", "4"); doPrint(printer); */ } void Part::doPrint( KPrinter& /*printer*/ ) { /* QPainter painter( &printer ); SplashColor paperColor; paperColor.rgb8 = splashMakeRGB8(0xff, 0xff, 0xff); QOutputDevKPrinter printdev( painter, paperColor, printer ); printdev.startDoc(m_doc->getXRef()); QValueList pages = printer.pageList(); for ( QValueList::ConstIterator i = pages.begin(); i != pages.end();) { m_docMutex.lock(); m_doc->displayPage(&printdev, *i, printer.resolution(), printer.resolution(), 0, true, true); if ( ++i != pages.end() ) printer.newPage(); m_docMutex.unlock(); } */ } -//temp -#include - -void Part::slotFind() -{ - KFindDialog dlg(widget()); - if (dlg.exec() == QDialog::Accepted) - document->find( false, dlg.pattern() ); -} - -void Part::slotFindNext() -{ - //if (!m_findText.isEmpty()) - document->find( true ); -} /* * BrowserExtension class */ BrowserExtension::BrowserExtension(Part* parent) : KParts::BrowserExtension( parent, "KPDF::BrowserExtension" ) { emit enableAction("print", true); setURLDropHandlingEnabled(true); } void BrowserExtension::print() { static_cast(parent())->slotPrint(); } // vim:ts=2:sw=2:tw=78:et diff --git a/kpdf/kpdf_part.h b/kpdf/kpdf_part.h index bd5dec135..2444d9407 100644 --- a/kpdf/kpdf_part.h +++ b/kpdf/kpdf_part.h @@ -1,158 +1,150 @@ /*************************************************************************** * Copyright (C) 2002 by Wilco Greven * * Copyright (C) 2003-2004 by Christophe Devriese * * * * Copyright (C) 2003 by Andy Goossens * * Copyright (C) 2003 by Laurent Montel * * Copyright (C) 2004 by Dominique Devriese * * Copyright (C) 2004 by Albert Astals Cid * * * * 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_PART_H_ #define _KPDF_PART_H_ -#include - #include #include class QWidget; class KAboutData; class KAction; class KPrinter; class KURL; class KToggleAction; class KSelectAction; class LinkAction; class LinkDest; class XOutputDev; class ThumbnailList; class PDFPartView; class KPDFDocument; namespace KPDF { class BrowserExtension; class PageWidget; /** * This is a "Part". It that does all the real work in a KPart * application. * * @short Main Part * @author Wilco Greven * @version 0.2 */ class Part : public KParts::ReadOnlyPart { Q_OBJECT public: /** * Default constructor */ Part(QWidget* parentWidget, const char* widgetName, QObject* parent, const char* name, const QStringList& args); /** * Destructor */ virtual ~Part(); static KAboutData* createAboutData(); // reimplemented from KParts::ReadOnlyPart bool closeURL(); - void displayPage(int pageNumber ); //TODO REMOVE ME! protected: // reimplemented from KParts::ReadOnlyPart virtual bool openFile(); void readSettings(); void writeSettings(); void updateAction(); void doPrint( KPrinter& printer ); protected slots: // connected to actions void slotGoToPage(); void slotPreviousPage(); void slotNextPage(); void slotGotoStart(); void slotGotoEnd(); void slotFind(); void slotFindNext(); void slotZoom( const QString& ); void slotZoomIn(); void slotZoomOut(); void slotFitToWidthToggled( bool ); void slotPrintPreview(); void slotToggleScrollBars( bool ); void slotToggleThumbnails( bool ); void slotSaveFileAs(); // can be connected do widget elements void updateActions(); public slots: // connected to Shell action (and browserExtension), not local one void slotPrint(); private: - -// PORT!! ### -// PDFDoc* m_doc; -QMutex m_docMutex; // REMOVE MEEE - // the document KPDFDocument * document; // main widgets ThumbnailList *m_thumbnailList; PageWidget *m_pageWidget; // static instances counter static unsigned int m_count; // actions KAction *m_gotoPage; KAction *m_prevPage; KAction *m_nextPage; KAction *m_firstPage; KAction *m_lastPage; KToggleAction *m_showScrollBars; KToggleAction *m_showPageList; KSelectAction *m_zoomTo; KToggleAction *m_fitToWidth; KAction *m_find; KAction *m_findNext; }; class BrowserExtension : public KParts::BrowserExtension { Q_OBJECT public: BrowserExtension(Part*); public slots: // Automatically detected by the host. void print(); }; } #endif // vim:ts=2:sw=2:tw=78:et diff --git a/kpdf/kpdf_shell.cpp b/kpdf/kpdf_shell.cpp index 40616b77d..1bb6ba678 100644 --- a/kpdf/kpdf_shell.cpp +++ b/kpdf/kpdf_shell.cpp @@ -1,222 +1,222 @@ /*************************************************************************** * Copyright (C) 2002 by Wilco Greven * * Copyright (C) 2002 by Chris Cheney * * Copyright (C) 2003 by Benjamin Meyer * * Copyright (C) 2003-2004 by Christophe Devriese * * * * Copyright (C) 2003 by Laurent Montel * * Copyright (C) 2003-2004 by Albert Astals Cid * * Copyright (C) 2003 by Luboš Luňák * * Copyright (C) 2003 by Malcolm Hunter * * Copyright (C) 2004 by Dominique Devriese * * Copyright (C) 2004 by Dirk Mueller * * * * 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 "kpdf_shell.h" #include "kpdf_shell.moc" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KPDF; Shell::Shell() : KParts::MainWindow(0, "KPDF::Shell") { // set the shell's ui resource file setXMLFile("kpdf_shell.rc"); // this routine will find and load our Part. it finds the Part by // name which is a bad idea usually.. but it's alright in this // case since our Part is made for this Shell KLibFactory *factory = KLibLoader::self()->factory("libkpdfpart"); if (factory) { // now that the Part is loaded, we cast it to a Part to get // our hands on it m_part = static_cast( factory->create(this, "kpdf_part", "KParts::ReadOnlyPart")); if (m_part) { // then, setup our actions setupActions(); // tell the KParts::MainWindow that this is indeed the main widget setCentralWidget(m_part->widget()); // and integrate the part's GUI with the shell's setupGUI(ToolBar | Keys | Save); createGUI(m_part); } } else { // if we couldn't find our Part, we exit since the Shell by // itself can't do anything useful KMessageBox::error(this, i18n("Unable to find kpdf part.")); kapp->quit(); // we return here, cause kapp->quit() only means "exit the // next time we enter the event loop... return; } - connect( m_part, SIGNAL( rightClick() ),SLOT( slotRMBClick() ) ); + //FIXME READD: connect( m_part, SIGNAL( rightClick() ),SLOT( slotRMBClick() ) ); readSettings(); } Shell::~Shell() { writeSettings(); } void Shell::openURL( const KURL & url ) { if ( m_part && m_part->openURL( url ) ) recent->addURL (url); } void Shell::readSettings() { recent->loadEntries( KGlobal::config() ); KGlobal::config()->setDesktopGroup(); bool fullScreen = KGlobal::config()->readBoolEntry( "FullScreen", false ); setFullScreen( fullScreen ); } void Shell::writeSettings() { saveMainWindowSettings(KGlobal::config(), "MainWindow"); recent->saveEntries( KGlobal::config() ); KGlobal::config()->setDesktopGroup(); KGlobal::config()->writeEntry( "FullScreen", m_fullScreenAction->isChecked()); KGlobal::config()->sync(); } void Shell::setupActions() { KStdAction::open(this, SLOT(fileOpen()), actionCollection()); recent = KStdAction::openRecent( this, SLOT( openURL( const KURL& ) ), actionCollection() ); KStdAction::print(m_part, SLOT(slotPrint()), actionCollection()); KStdAction::quit(this, SLOT(slotQuit()), actionCollection()); setStandardToolBarMenuEnabled(true); m_showMenuBarAction = KStdAction::showMenubar( this, SLOT( slotShowMenubar() ), actionCollection(), "options_show_menubar" ); m_fullScreenAction = KStdAction::fullScreen( this, SLOT( slotUpdateFullScreen() ), actionCollection(), this ); m_popup = new KPopupMenu( this, "rmb popup" ); m_popup->insertTitle( i18n( "Full Screen Options" ) ); m_fullScreenAction->plug( m_popup ); m_popup->insertTitle( i18n( "Tools" ) ); m_showMenuBarAction->plug( m_popup ); } void Shell::saveProperties(KConfig* config) { // the 'config' object points to the session managed // config file. anything you write here will be available // later when this app is restored config->writePathEntry( "URL", m_part->url().url() ); } void Shell::slotShowMenubar() { if ( m_showMenuBarAction->isChecked() ) menuBar()->show(); else menuBar()->hide(); } void Shell::readProperties(KConfig* config) { // the 'config' object points to the session managed // config file. this function is automatically called whenever // the app is being restored. read in here whatever you wrote // in 'saveProperties' KURL url ( config->readPathEntry( "URL" ) ); if ( url.isValid() ) openURL( url ); } void Shell::fileOpen() { // this slot is called whenever the File->Open menu is selected, // the Open shortcut is pressed (usually CTRL+O) or the Open toolbar // button is clicked KURL url = KFileDialog::getOpenURL( QString::null, "application/pdf" );//getOpenFileName(); if (!url.isEmpty()) openURL(url); } void Shell::applyNewToolbarConfig() { applyMainWindowSettings(KGlobal::config(), "MainWindow"); } void Shell::slotQuit() { kapp->closeAllWindows(); } void Shell::setFullScreen( bool useFullScreen ) { if( useFullScreen ) showFullScreen(); else showNormal(); } void Shell::slotUpdateFullScreen() { if( m_fullScreenAction->isChecked()) { menuBar()->hide(); toolBar()->hide(); //todo fixme showFullScreen(); #if 0 kapp->installEventFilter( m_fsFilter ); if ( m_gvpart->document()->isOpen() ) slotFitToPage(); #endif } else { //kapp->removeEventFilter( m_fsFilter ); menuBar()->show(); toolBar()->show(); showNormal(); } } void Shell::slotRMBClick() { m_popup->exec( QCursor::pos() ); } // vim:ts=2:sw=2:tw=78:et diff --git a/kpdf/page.cpp b/kpdf/page.cpp index 07b4cae0b..a60808e79 100644 --- a/kpdf/page.cpp +++ b/kpdf/page.cpp @@ -1,122 +1,122 @@ /*************************************************************************** * Copyright (C) 2004 by Enrico Ros * * * * 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 includes #include #include #include // local includes #include "TextOutputDev.h" #include "page.h" KPDFPage::KPDFPage( uint i, float w, float h ) : m_number( i ), m_width( w ), m_height( h ), m_zoom( 1 ), m_pixmap( 0 ), m_thumbnail( 0 ), m_text( 0 ), m_overlay( 0 ) { - printf( "hello %d\n", i ); + printf( "hello %d ", i ); } KPDFPage::~KPDFPage() { printf( "bye[%d] ", m_number ); delete m_pixmap; delete m_thumbnail; delete m_text; delete m_overlay; } /** DRAWING **/ -void KPDFPage::drawPixmap( QPainter * p, const QRect & limits ) // MUTEXED +void KPDFPage::drawPixmap( QPainter * p, const QRect & limits ) const // MUTEXED { - threadLock.lock(); + //threadLock.lock(); if ( m_pixmap ) p->drawPixmap( limits.topLeft(), *m_pixmap, limits ); else p->fillRect( limits, Qt::blue ); - threadLock.unlock(); + //threadLock.unlock(); } -void KPDFPage::drawThumbnail( QPainter * p ) // MUTEXED +void KPDFPage::drawThumbnail( QPainter * p, const QRect & limits ) const // MUTEXED { - threadLock.lock(); + //threadLock.lock(); if ( m_thumbnail ) - p->drawPixmap( 0,0, *m_thumbnail ); + p->drawPixmap( limits.topLeft(), *m_thumbnail, limits ); else - p->fillRect( 10,10, 20,20, Qt::red ); + p->fillRect( limits, Qt::red ); - threadLock.unlock(); + //threadLock.unlock(); } /** FIND **/ bool KPDFPage::hasText( QString & text ) { //FIXME return text.isNull(); } /* const QRect & KPDFPage::textPosition() { //FIXME return QRect(); } */ /** SLOTS **/ void KPDFPage::slotSetZoom( float /*scale*/ ) { } void KPDFPage::slotSetContents( QPixmap * pix ) // MUTEXED { if ( !pix ) return; threadLock.lock(); delete m_pixmap; m_pixmap = new QPixmap( pix->width() + 6, pix->height() + 6 ); bitBlt( m_pixmap, 1,1, pix, 0,0, pix->width(),pix->height() ); QPainter paint( m_pixmap ); paint.drawRect( 0,0, pix->width()+1, pix->height()+1 ); paint.end(); //update page size (in pixels) m_size = m_pixmap->size(); threadLock.unlock(); } void KPDFPage::slotSetThumbnail( QPixmap * thumb ) // MUTEXED { if ( !thumb ) return; threadLock.lock(); delete m_thumbnail; m_thumbnail = new QPixmap( *thumb ); threadLock.unlock(); } void KPDFPage::slotSetOverlay() // MUTEXED { threadLock.lock(); //TODO this threadLock.unlock(); } #include "page.moc" diff --git a/kpdf/page.h b/kpdf/page.h index 37b81af6c..cecbc3bfe 100644 --- a/kpdf/page.h +++ b/kpdf/page.h @@ -1,82 +1,83 @@ /*************************************************************************** * Copyright (C) 2004 by Enrico Ros * * * * 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_PAGE_H_ #define _KPDF_PAGE_H_ #include #include #include #include "document.h" class QPixmap; class QString; class QRect; class TextOutputDev; class PageOverlay{ /*fake temp*/ }; /** * @short Collector for all the data belonging to a page. * * The Page class contains its pixmap, the thumbnail, a search page (a class * used internally for searching data) the modifiers descriptors (for overlay * graphics) and more. * It provides accessor methods for all those operations too. * * Note: add stuff this class contains is destroyed automatically when the * class is destroyed. */ class KPDFPage : public QObject, public KPDFDocumentObserver { Q_OBJECT public: KPDFPage( uint number, float width, float height ); ~KPDFPage(); - // query methods + // page properties uint number() const { return m_number; } float width() const { return m_width; } float height() const { return m_height; } float ratio() const { return m_height / m_width; } + // rendering + void drawPixmap( QPainter * p, const QRect & rect ) const; + void drawThumbnail( QPainter * p, const QRect & rect ) const; float currentZoom() const { return m_zoom; } const QSize & currentSize() const { return m_size; } - void drawPixmap( QPainter * p, const QRect & rect ); - void drawThumbnail( QPainter * p ); // find related methods bool hasText( QString & text ); //const QRect & textPosition(); signals: void changed( KPDFPage * thisPage ); private slots: void slotSetZoom( float scale ); void slotSetContents( QPixmap * ); void slotSetThumbnail( QPixmap * ); void slotSetOverlay( /*..DOMdescription..*/ ); private: QMutex threadLock; uint m_number; float m_width, m_height; float m_zoom; QSize m_size; QPixmap * m_pixmap; QPixmap * m_thumbnail; TextOutputDev * m_text; PageOverlay * m_overlay; }; #endif diff --git a/kpdf/thumbnaillist.cpp b/kpdf/thumbnaillist.cpp index 2df8ba457..afc2b6841 100644 --- a/kpdf/thumbnaillist.cpp +++ b/kpdf/thumbnaillist.cpp @@ -1,177 +1,205 @@ /*************************************************************************** * Copyright (C) 2004 by Albert Astals Cid * * * * 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 #include "PDFDoc.h" #include "thumbnailgenerator.h" #include "thumbnaillist.h" #include "thumbnail.h" -ThumbnailList::ThumbnailList(QWidget *parent, QMutex *docMutex) : QTable(parent), m_tg(0), m_doc(0), m_docMutex(docMutex) +#include "page.h" + +ThumbnailList::ThumbnailList(QWidget *parent, KPDFDocument *document) : QTable(parent), m_document(document) { setNumCols(1); + setColumnStretchable(0,true); setLeftMargin(0); setTopMargin(0); setHScrollBarMode(QScrollView::AlwaysOff); + setVScrollBarMode(QScrollView::AlwaysOn); //TODO soon restore autoscaling behavior + setReadOnly(true); m_selected = 0; - - connect(this, SIGNAL(pressed(int, int, int, const QPoint&)), this, SLOT(emitClicked(int))); - connect(this, SIGNAL(currentChanged(int, int)), this, SLOT(emitClicked(int))); - connect(this, SIGNAL(currentChanged(int, int)), this, SLOT(changeSelected(int))); + + connect(this, SIGNAL(currentChanged(int, int)), document, SLOT(slotSetCurrentPage(int))); } -ThumbnailList::~ThumbnailList() +void ThumbnailList::pageSetup( const QValueList & pages ) { - if (m_tg) + // delete old 'Thumbnail' objects + for (int i=0; i < numRows(); i++) + clearCellWidget( i, 0 ); + + // generate new 'Thumbnail' objects + Thumbnail *t; + //viewport()->setUpdatesEnabled(false); + setNumRows( pages.count() ); + //viewport()->setUpdatesEnabled(true); + uint i = 0; + int width = columnWidth(0); + QValueList::const_iterator it = pages.begin(); + QValueList::const_iterator end = pages.end(); + for (; it != end ; ++it) { - m_tg->wait(); - delete m_tg; + int pageNumber = *it; + t = new Thumbnail( this, QString::number(pageNumber+1), viewport()->paletteBackgroundColor(), + (int)(width * m_document->page(pageNumber)->ratio()), width); + setCellWidget(i, 0, t); + setRowHeight(i++, t->sizeHint().height()); } -} -void ThumbnailList::setCurrentThumbnail(int i) -{ - setCurrentCell(i-1, 0); - changeSelected(i-1); + // request for thumbnail generation + //FIXME document->requestThumbnail... + //or generateThumbnails(d->pdfdoc); } -void ThumbnailList::changeSelected(int i) +void ThumbnailList::pageSetCurrent( int pageNumber, float /*position*/ ) { + setCurrentCell( pageNumber, 0 ); +printf("current:%d\n",pageNumber); Thumbnail *t; t = dynamic_cast(cellWidget(m_selected, 0)); if (t) t -> setSelected(false); - m_selected = i; + m_selected = pageNumber; t = dynamic_cast(cellWidget(m_selected, 0)); if (t) t -> setSelected(true); } +/** TO BE IMPORTED: + void generateThumbnails(PDFDoc *doc); + void stopThumbnailGeneration(); +protected slots: + void customEvent(QCustomEvent *e); +private slots: + void changeSelected(int i); + void emitClicked(int i); +signals: + void clicked(int); +private: + void generateNextThumbnail(); + ThumbnailGenerator *m_tg; + + void resizeThumbnails(); + int m_nextThumbnail; + bool m_ignoreNext; + +DELETE: +if (m_tg) +{ + m_tg->wait(); + delete m_tg; +} + void ThumbnailList::generateThumbnails(PDFDoc *doc) { m_nextThumbnail = 1; m_doc = doc; generateNextThumbnail(); } void ThumbnailList::generateNextThumbnail() { if (m_tg) { m_tg->wait(); delete m_tg; } m_tg = new ThumbnailGenerator(m_doc, m_docMutex, m_nextThumbnail, QPaintDevice::x11AppDpiX(), this); m_tg->start(); } + void ThumbnailList::stopThumbnailGeneration() { if (m_tg) { m_ignoreNext = true; m_tg->wait(); delete m_tg; m_tg = 0; } } + void ThumbnailList::customEvent(QCustomEvent *e) { if (e->type() == 65432 && !m_ignoreNext) { QImage *i = (QImage*)(e -> data()); setThumbnail(m_nextThumbnail, i); m_nextThumbnail++; if (m_nextThumbnail <= m_doc->getNumPages()) generateNextThumbnail(); else { m_tg->wait(); delete m_tg; m_tg = 0; } } m_ignoreNext = false; } +*/ -void ThumbnailList::setPages(int i, double ar) -{ - Thumbnail *t; - m_ar = ar; - setNumRows(i); - for(int j=1; j <= i; j++) - { - t = new Thumbnail(this, QString::number(j), viewport()->paletteBackgroundColor(), (int)(visibleWidth()*ar), visibleWidth()); - setCellWidget(j-1, 0, t); - setRowHeight(j-1, t->sizeHint().height()); - } - m_heightLimit = 0; -} - -void ThumbnailList::setThumbnail(int i, const QImage *thumbnail) +void ThumbnailList::notifyThumbnailChanged( int pageNumber ) { - Thumbnail *t; - t = dynamic_cast(cellWidget(i-1, 0)); - t->setImage(thumbnail); + Thumbnail *t = dynamic_cast(cellWidget(pageNumber, 0)); + if ( t && viewport()->rect().intersects( t->rect() ) ) + t->update(); } -void ThumbnailList::viewportResizeEvent(QResizeEvent *) +void ThumbnailList::viewportResizeEvent(QResizeEvent *e) { + printf("%d\n",e->size().width()); // that if are here to avoid recursive resizing of death // where the user makes the window smaller, that makes appear // the vertical scrollbar, that makes thumbnails smaller, and // while they get smaller the vertical scrollbar is not needed // and ... // ... it also works for when the user makes the window larger // and then the scrollbar disappears but that makes thumbnails // larger and then scrollbar reappears and ... +/* Thumbnail *t; if (numRows() == 0) return; t = dynamic_cast(cellWidget(0, 0)); if (size().height() <= m_heightLimit) { if (t->getImageHeight() > (int)(visibleWidth()*m_ar)) { setColumnWidth(0, visibleWidth()); resizeThumbnails(); } } else { if (visibleWidth() != columnWidth(0)) { setColumnWidth(0, visibleWidth()); resizeThumbnails(); if (size().height() > m_heightLimit && verticalScrollBar() -> isVisible()) { m_heightLimit = (int) ceil(numRows() * ((visibleWidth() + verticalScrollBar() -> width()) * m_ar + t -> labelSizeHintHeight())); } } } -} - -void ThumbnailList::resizeThumbnails() -{ + //THE "resizeThumbnails" method: ##IMPORT## Thumbnail *t; for(int i = 0; i < numRows(); ++i) { t = dynamic_cast(cellWidget(i, 0)); t->setImageSize((int)(visibleWidth()*m_ar), visibleWidth()); setRowHeight(i, (int)(visibleWidth()*m_ar) + t->labelSizeHintHeight()); } -} - -void ThumbnailList::emitClicked(int i) -{ - emit clicked(i+1); +*/ } #include "thumbnaillist.moc" diff --git a/kpdf/thumbnaillist.h b/kpdf/thumbnaillist.h index f20ca37c6..8007600af 100644 --- a/kpdf/thumbnaillist.h +++ b/kpdf/thumbnaillist.h @@ -1,76 +1,36 @@ /*************************************************************************** * Copyright (C) 2004 by Albert Astals Cid * * * * 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 THUMBNAILLIST_H #define THUMBNAILLIST_H #include #include "document.h" -class QImage; - -class PDFDoc; - -class ThumbnailGenerator; class ThumbnailList : public QTable, public KPDFDocumentObserver { Q_OBJECT public: - ThumbnailList(QWidget *parent, QMutex *docMutex); - ~ThumbnailList(); + ThumbnailList(QWidget *parent, KPDFDocument *document); - // inherited as KPDFDocumentObserver - void pageSetup( const QValueList & pages ) - { - // TODO use a qvaluelist to pass aspect ratio? - // TODO move it the thumbnail list? - setPages( pages.count(), 2 ); - //generateThumbnails(d->pdfdoc); - } - void pageSetCurrent( int /*pageNumber*/, float /*position*/ ) - { - //setCurrentThumbnail(m_currentPage); - } + // inherited as a KPDFDocumentObserver + void pageSetup( const QValueList & pages ); + void pageSetCurrent( int pageNumber, float position ); + void notifyThumbnailChanged( int pageNumber ); - void setCurrentThumbnail(int i); - void setPages(int i, double ar); - - void generateThumbnails(PDFDoc *doc); - void stopThumbnailGeneration(); - - protected slots: - void customEvent(QCustomEvent *e); - - private slots: - void changeSelected(int i); - void emitClicked(int i); - - signals: - void clicked(int); - protected: void viewportResizeEvent(QResizeEvent *); - + private: - void generateNextThumbnail(); - void resizeThumbnails(); - void setThumbnail(int i, const QImage *thumbnail); - - double m_ar; int m_selected; - int m_heightLimit; - int m_nextThumbnail; - ThumbnailGenerator *m_tg; - PDFDoc *m_doc; - QMutex *m_docMutex; - bool m_ignoreNext; + KPDFDocument *m_document; }; #endif