You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegraphics/kpdf/ui/pageview.cpp

2171 lines
81 KiB

/***************************************************************************
* Copyright (C) 2004 by Enrico Ros <eros.kde@email.it> *
* Copyright (C) 2004-2006 by Albert Astals Cid <tsdgeos@terra.es> *
* *
* With portions of code from kpdf/kpdf_pagewidget.cc by: *
* Copyright (C) 2002 by Wilco Greven <greven@kde.org> *
* Copyright (C) 2003 by Christophe Devriese *
* <Christophe.Devriese@student.kuleuven.ac.be> *
* Copyright (C) 2003 by Laurent Montel <montel@kde.org> *
* Copyright (C) 2003 by Dirk Mueller <mueller@kde.org> *
* Copyright (C) 2004 by James Ots <kde@jamesots.com> *
* *
* 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 <tqcursor.h>
#include <tqpainter.h>
#include <tqtimer.h>
#include <tqdatetime.h>
#include <tqpushbutton.h>
#include <tqtooltip.h>
#include <tqapplication.h>
#include <tqclipboard.h>
#include <dcopclient.h>
#include <kcursor.h>
#include <kiconloader.h>
#include <kurldrag.h>
#include <tdeaction.h>
#include <tdestdaccel.h>
#include <tdeactioncollection.h>
#include <tdepopupmenu.h>
#include <tdelocale.h>
#include <tdefiledialog.h>
#include <kimageeffect.h>
#include <kimageio.h>
#include <tdeapplication.h>
#include <kdebug.h>
// system includes
#include <math.h>
#include <stdlib.h>
// local includes
#include "pageview.h"
#include "pageviewutils.h"
#include "pagepainter.h"
#include "core/document.h"
#include "core/page.h"
#include "core/link.h"
#include "core/generator.h"
#include "conf/settings.h"
#define ROUND(x) (int(x + 0.5))
// definition of searchID for this class
#define PAGEVIEW_SEARCH_ID 2
// structure used internally by PageView for data storage
class PageViewPrivate
{
public:
// the document, pageviewItems and the 'visible cache'
KPDFDocument * document;
TQValueVector< PageViewItem * > items;
TQValueList< PageViewItem * > visibleItems;
// view layout (columns and continuous in Settings), zoom and mouse
PageView::ZoomMode zoomMode;
float zoomFactor;
int rotation;
PageView::MouseMode mouseMode;
TQPoint mouseGrabPos;
TQPoint mousePressPos;
int mouseMidStartY;
bool mouseOnRect;
TQRect mouseSelectionRect;
TQColor selectionRectColor;
// type ahead find
bool typeAheadActive;
TQString typeAheadString;
TQTimer * findTimeoutTimer;
// viewport move
bool viewportMoveActive;
TQTime viewportMoveTime;
TQPoint viewportMoveDest;
TQTimer * viewportMoveTimer;
// auto scroll
int scrollIncrement;
TQTimer * autoScrollTimer;
// other stuff
TQTimer * delayResizeTimer;
bool dirtyLayout;
bool blockViewport; // prevents changes to viewport
bool blockPixmapsRequest; // prevent pixmap requests
PageViewMessage * messageWindow; // in pageviewutils.h
PageViewTip * tip;
// drag scroll
TQPoint dragScrollVector;
TQTimer dragScrollTimer;
// actions
TDEToggleAction * aMouseNormal;
TDEToggleAction * aMouseSelect;
TDEToggleAction * aMouseEdit;
TDESelectAction * aZoom;
TDEToggleAction * aZoomFitWidth;
TDEToggleAction * aZoomFitPage;
TDEToggleAction * aZoomFitText;
TDEToggleAction * aViewTwoPages;
TDEToggleAction * aViewContinuous;
TDEAction * aPrevAction;
};
class PageViewTip : public TQToolTip
{
public:
PageViewTip( PageView * view )
: TQToolTip( view->viewport() ), m_view( view )
{
}
~PageViewTip()
{
remove( m_view->viewport() );
}
protected:
void maybeTip( const TQPoint &p );
private:
PageView * m_view;
};
void PageViewTip::maybeTip( const TQPoint &_p )
{
TQPoint p( _p.x() + m_view->contentsX(), _p.y() + m_view->contentsY() );
PageViewItem * pageItem = m_view->pickItemOnPoint( p.x(), p.y() );
if ( pageItem && m_view->d->mouseMode == PageView::MouseNormal )
{
double nX = (double)(p.x() - pageItem->geometry().left()) / (double)pageItem->width(),
nY = (double)(p.y() - pageItem->geometry().top()) / (double)pageItem->height();
// if over a ObjectRect (of type Link) change cursor to hand
const ObjectRect * object = pageItem->page()->hasObject( ObjectRect::Link, nX, nY );
if ( object )
{
// set tooltip over link's rect
KPDFLink *link = (KPDFLink *)object->pointer();
TQString strtip = link->linkTip();
if ( !strtip.isEmpty() )
{
TQRect linkRect = object->geometry( pageItem->width(), pageItem->height() );
linkRect.moveBy( - m_view->contentsX() + pageItem->geometry().left(), - m_view->contentsY() + pageItem->geometry().top() );
tip( linkRect, strtip );
}
}
}
}
/* PageView. What's in this file? -> quick overview.
* Code weight (in rows) and meaning:
* 160 - constructor and creating actions plus their connected slots (empty stuff)
* 70 - DocumentObserver inherited methodes (important)
* 550 - events: mouse, keyboard, drag/drop
* 170 - slotRelayoutPages: set contents of the scrollview on continuous/single modes
* 100 - zoom: zooming pages in different ways, keeping update the toolbar actions, etc..
* other misc functions: only slotRequestVisiblePixmaps and pickItemOnPoint noticeable,
* and many insignificant stuff like this comment :-)
*/
PageView::PageView( TQWidget *parent, KPDFDocument *document )
: TQScrollView( parent, "KPDF::pageView", WStaticContents | WNoAutoErase )
{
// create and initialize private storage structure
d = new PageViewPrivate();
d->document = document;
d->zoomMode = (PageView::ZoomMode)KpdfSettings::zoomMode();
d->zoomFactor = KpdfSettings::zoomFactor();
d->rotation = 0;
d->mouseMode = MouseNormal;
d->mouseMidStartY = -1;
d->mouseOnRect = false;
d->typeAheadActive = false;
d->findTimeoutTimer = 0;
d->viewportMoveActive = false;
d->viewportMoveTimer = 0;
d->scrollIncrement = 0;
d->autoScrollTimer = 0;
d->delayResizeTimer = 0;
d->dirtyLayout = false;
d->blockViewport = false;
d->blockPixmapsRequest = false;
d->messageWindow = new PageViewMessage(this);
d->tip = new PageViewTip( this );
d->aPrevAction = 0;
// widget setup: setup focus, accept drops and track mouse
viewport()->setFocusProxy( this );
viewport()->setFocusPolicy( TQ_StrongFocus );
//viewport()->setPaletteBackgroundColor( TQt::white );
viewport()->setBackgroundMode( TQt::NoBackground );
setResizePolicy( Manual );
setAcceptDrops( true );
setDragAutoScroll( false );
viewport()->setMouseTracking( true );
// conntect the padding of the viewport to pixmaps requests
connect( this, TQT_SIGNAL(contentsMoving(int, int)), this, TQT_SLOT(slotRequestVisiblePixmaps(int, int)) );
connect( &d->dragScrollTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotDragScroll()) );
// set a corner button to resize the view to the page size
// TQPushButton * resizeButton = new TQPushButton( viewport() );
// resizeButton->setPixmap( SmallIcon("crop") );
// setCornerWidget( resizeButton );
// resizeButton->setEnabled( false );
// connect(...);
setInputMethodEnabled( true );
// schedule the welcome message
TQTimer::singleShot( 0, this, TQT_SLOT( slotShowWelcome() ) );
}
PageView::~PageView()
{
// delete all widgets
TQValueVector< PageViewItem * >::iterator dIt = d->items.begin(), dEnd = d->items.end();
for ( ; dIt != dEnd; ++dIt )
delete *dIt;
delete d->tip;
d->tip = 0;
d->document->removeObserver( this );
delete d;
}
void PageView::setupActions( TDEActionCollection * ac )
{
// Zoom actions ( higher scales takes lots of memory! )
d->aZoom = new TDESelectAction( i18n( "Zoom" ), "viewmag", 0, TQT_TQOBJECT(this), TQT_SLOT( slotZoom() ), ac, "zoom_to" );
d->aZoom->setEditable( true );
#if KDE_IS_VERSION(3,4,89)
d->aZoom->setMaxComboViewCount( 13 );
#endif
updateZoomText();
KStdAction::zoomIn( TQT_TQOBJECT(this), TQT_SLOT( slotZoomIn() ), ac, "zoom_in" );
KStdAction::zoomOut( TQT_TQOBJECT(this), TQT_SLOT( slotZoomOut() ), ac, "zoom_out" );
d->aZoomFitWidth = new TDEToggleAction( i18n("Fit to Page &Width"), "view_fit_width", 0, ac, "zoom_fit_width" );
connect( d->aZoomFitWidth, TQT_SIGNAL( toggled( bool ) ), TQT_SLOT( slotFitToWidthToggled( bool ) ) );
d->aZoomFitPage = new TDEToggleAction( i18n("Fit to &Page"), "view_fit_window", 0, ac, "zoom_fit_page" );
connect( d->aZoomFitPage, TQT_SIGNAL( toggled( bool ) ), TQT_SLOT( slotFitToPageToggled( bool ) ) );
d->aZoomFitText = new TDEToggleAction( i18n("Fit to &Text"), "zoom-fit-best", 0, ac, "zoom_fit_text" );
connect( d->aZoomFitText, TQT_SIGNAL( toggled( bool ) ), TQT_SLOT( slotFitToTextToggled( bool ) ) );
// rotate actions
TDEAction *action;
action = new TDEAction( i18n("Rotate Right"), "object-rotate-right", TDEShortcut( "Ctrl+Shift++" ),
TQT_TQOBJECT(this), TQT_SLOT( slotRotateRight() ), ac, "rotate_right" );
action = new TDEAction( i18n("Rotate Left"), "object-rotate-left", TDEShortcut( "Ctrl+Shift+-" ),
TQT_TQOBJECT(this), TQT_SLOT( slotRotateLeft() ), ac, "rotate_left" );
// View-Layout actions
d->aViewTwoPages = new TDEToggleAction( i18n("&Two Pages"), "view_left_right", 0, ac, "view_twopages" );
connect( d->aViewTwoPages, TQT_SIGNAL( toggled( bool ) ), TQT_SLOT( slotTwoPagesToggled( bool ) ) );
d->aViewTwoPages->setChecked( KpdfSettings::viewColumns() > 1 );
d->aViewContinuous = new TDEToggleAction( i18n("&Continuous"), "view_text", 0, ac, "view_continuous" );
connect( d->aViewContinuous, TQT_SIGNAL( toggled( bool ) ), TQT_SLOT( slotContinuousToggled( bool ) ) );
d->aViewContinuous->setChecked( KpdfSettings::viewContinuous() );
// Mouse-Mode actions
d->aMouseNormal = new TDERadioAction( i18n("&Browse Tool"), "input-mouse", 0, TQT_TQOBJECT(this), TQT_SLOT( slotSetMouseNormal() ), ac, "mouse_drag" );
d->aMouseNormal->setExclusiveGroup( "MouseType" );
d->aMouseNormal->setChecked( true );
TDEToggleAction * mz = new TDERadioAction( i18n("&Zoom Tool"), "viewmag", 0, TQT_TQOBJECT(this), TQT_SLOT( slotSetMouseZoom() ), ac, "mouse_zoom" );
mz->setExclusiveGroup( "MouseType" );
d->aMouseSelect = new TDERadioAction( i18n("&Select Tool"), "frame_edit", 0, TQT_TQOBJECT(this), TQT_SLOT( slotSetMouseSelect() ), ac, "mouse_select" );
d->aMouseSelect->setExclusiveGroup( "MouseType" );
/* d->aMouseEdit = new TDERadioAction( i18n("Draw"), "edit", 0, TQT_TQOBJECT(this), TQT_SLOT( slotSetMouseDraw() ), ac, "mouse_draw" );
d->aMouseEdit->setExclusiveGroup("MouseType");
d->aMouseEdit->setEnabled( false ); // implement feature before removing this line*/
// Other actions
TDEAction * su = new TDEAction( i18n("Scroll Up"), 0, TQT_TQOBJECT(this), TQT_SLOT( slotScrollUp() ), ac, "view_scroll_up" );
su->setShortcut( "Shift+Up" );
TDEAction * sd = new TDEAction( i18n("Scroll Down"), 0, TQT_TQOBJECT(this), TQT_SLOT( slotScrollDown() ), ac, "view_scroll_down" );
sd->setShortcut( "Shift+Down" );
}
bool PageView::canFitPageWidth()
{
return d->zoomMode != ZoomFitWidth;
}
void PageView::fitPageWidth( int /*page*/ )
{
d->aZoom->setCurrentItem(0);
slotZoom();
}
//BEGIN DocumentObserver inherited methods
void PageView::notifySetup( const TQValueVector< KPDFPage * > & pageSet, bool documentChanged )
{
// reuse current pages if nothing new
if ( ( pageSet.count() == d->items.count() ) && !documentChanged )
{
int count = pageSet.count();
for ( int i = 0; (i < count) && !documentChanged; i++ )
if ( (int)pageSet[i]->number() != d->items[i]->pageNumber() )
documentChanged = true;
if ( !documentChanged )
return;
}
// delete all widgets (one for each page in pageSet)
TQValueVector< PageViewItem * >::iterator dIt = d->items.begin(), dEnd = d->items.end();
for ( ; dIt != dEnd; ++dIt )
delete *dIt;
d->items.clear();
d->visibleItems.clear();
// create children widgets
TQValueVector< KPDFPage * >::const_iterator setIt = pageSet.begin(), setEnd = pageSet.end();
for ( ; setIt != setEnd; ++setIt )
d->items.push_back( new PageViewItem( *setIt ) );
if ( pageSet.count() > 0 )
// TODO for Enrico: Check if doing always the slotRelayoutPages() is not
// suboptimal in some cases, i'd say it is not but a recheck will not hurt
// Need slotRelayoutPages() here instead of d->dirtyLayout = true
// because opening a pdf from another pdf will not trigger a viewportchange
// so pages are never relayouted
TQTimer::singleShot(0, this, TQT_SLOT(slotRelayoutPages()));
else
{
// update the mouse cursor when closing because we may have close through a link and
// want the cursor to come back to the normal cursor
updateCursor( viewportToContents( mapFromGlobal( TQCursor::pos() ) ) );
resizeContents( 0, 0 );
}
// OSD to display pages
if ( documentChanged && pageSet.count() > 0 && KpdfSettings::showOSD() )
d->messageWindow->display(
i18n(" Loaded a one-page document.",
" Loaded a %n-page document.",
pageSet.count() ),
PageViewMessage::Info, 4000 );
}
void PageView::notifyViewportChanged( bool smoothMove )
{
// if we are the one changing viewport, skip this nofity
if ( d->blockViewport )
return;
// block setViewport outgoing calls
d->blockViewport = true;
// find PageViewItem matching the viewport description
const DocumentViewport & vp = d->document->viewport();
PageViewItem * item = 0;
TQValueVector< PageViewItem * >::iterator iIt = d->items.begin(), iEnd = d->items.end();
for ( ; iIt != iEnd; ++iIt )
if ( (*iIt)->pageNumber() == vp.pageNumber )
{
item = *iIt;
break;
}
if ( !item )
{
kdDebug() << "viewport has no matching item!" << endl;
d->blockViewport = false;
return;
}
// relayout in "Single Pages" mode or if a relayout is pending
d->blockPixmapsRequest = true;
if ( !KpdfSettings::viewContinuous() || d->dirtyLayout )
slotRelayoutPages();
// restore viewport center or use default {x-center,v-top} alignment
const TQRect & r = item->geometry();
int newCenterX = r.left(),
newCenterY = r.top();
if ( vp.rePos.enabled )
{
if (vp.rePos.pos == DocumentViewport::Center)
{
newCenterX += (int)( vp.rePos.normalizedX * (double)r.width() );
newCenterY += (int)( vp.rePos.normalizedY * (double)r.height() );
}
else
{
// TopLeft
newCenterX += (int)( vp.rePos.normalizedX * (double)r.width() + viewport()->width() / 2 );
newCenterY += (int)( vp.rePos.normalizedY * (double)r.height() + viewport()->height() / 2 );
}
}
else
{
newCenterX += r.width() / 2;
newCenterY += visibleHeight() / 2 - 10;
}
// if smooth movement requested, setup parameters and start it
if ( smoothMove )
{
d->viewportMoveActive = true;
d->viewportMoveTime.start();
d->viewportMoveDest.setX( newCenterX );
d->viewportMoveDest.setY( newCenterY );
if ( !d->viewportMoveTimer )
{
d->viewportMoveTimer = new TQTimer( this );
connect( d->viewportMoveTimer, TQT_SIGNAL( timeout() ),
this, TQT_SLOT( slotMoveViewport() ) );
}
d->viewportMoveTimer->start( 25 );
verticalScrollBar()->setEnabled( false );
horizontalScrollBar()->setEnabled( false );
}
else
center( newCenterX, newCenterY );
d->blockPixmapsRequest = false;
// request visible pixmaps in the current viewport and recompute it
slotRequestVisiblePixmaps();
// enable setViewport calls
d->blockViewport = false;
// update zoom text if in a ZoomFit/* zoom mode
if ( d->zoomMode != ZoomFixed )
updateZoomText();
// since the page has moved below cursor, update it
updateCursor( viewportToContents( mapFromGlobal( TQCursor::pos() ) ) );
}
void PageView::notifyPageChanged( int pageNumber, int changedFlags )
{
// only handle pixmap / highlight changes notifies
if ( changedFlags & DocumentObserver::Bookmark )
return;
// iterate over visible items: if page(pageNumber) is one of them, repaint it
TQValueList< PageViewItem * >::iterator iIt = d->visibleItems.begin(), iEnd = d->visibleItems.end();
for ( ; iIt != iEnd; ++iIt )
if ( (*iIt)->pageNumber() == pageNumber )
{
// update item's rectangle plus the little outline
TQRect expandedRect = (*iIt)->geometry();
expandedRect.addCoords( -1, -1, 3, 3 );
updateContents( expandedRect );
// if we were "zoom-dragging" do not overwrite the "zoom-drag" cursor
if ( cursor().shape() != TQt::SizeVerCursor )
{
// since the page has been regenerated below cursor, update it
updateCursor( viewportToContents( mapFromGlobal( TQCursor::pos() ) ) );
}
break;
}
}
void PageView::notifyContentsCleared( int changedFlags )
{
// if pixmaps were cleared, re-ask them
if ( changedFlags & DocumentObserver::Pixmap )
slotRequestVisiblePixmaps();
}
bool PageView::canUnloadPixmap( int pageNumber )
{
// if the item is visible, forbid unloading
TQValueList< PageViewItem * >::iterator vIt = d->visibleItems.begin(), vEnd = d->visibleItems.end();
for ( ; vIt != vEnd; ++vIt )
if ( (*vIt)->pageNumber() == pageNumber )
return false;
// if hidden premit unloading
return true;
}
//END DocumentObserver inherited methods
void PageView::showText( const TQString &text, int ms )
{
d->messageWindow->display(text, PageViewMessage::Info, ms );
}
//BEGIN widget events
void PageView::viewportPaintEvent( TQPaintEvent * pe )
{
// create the rect into contents from the clipped screen rect
TQRect viewportRect = viewport()->rect();
TQRect contentsRect = pe->rect().intersect( viewportRect );
contentsRect.moveBy( contentsX(), contentsY() );
if ( !contentsRect.isValid() )
return;
// create the screen painter. a pixel painted ar contentsX,contentsY
// appears to the top-left corner of the scrollview.
TQPainter screenPainter( viewport(), true );
screenPainter.translate( -contentsX(), -contentsY() );
// selectionRect is the normalized mouse selection rect
TQRect selectionRect = d->mouseSelectionRect;
if ( !selectionRect.isNull() )
selectionRect = selectionRect.normalize();
// selectionRectInternal without the border
TQRect selectionRectInternal = selectionRect;
selectionRectInternal.addCoords( 1, 1, -1, -1 );
// color for blending
TQColor selBlendColor = (selectionRect.width() > 8 || selectionRect.height() > 8) ?
d->selectionRectColor : TQt::red;
// subdivide region into rects
TQMemArray<TQRect> allRects = TQRegion(pe->region()).rects();
uint numRects = allRects.count();
// preprocess rects area to see if it worths or not using subdivision
uint summedArea = 0;
for ( uint i = 0; i < numRects; i++ )
{
const TQRect & r = allRects[i];
summedArea += r.width() * r.height();
}
// very elementary check: SUMj(Region[j].area) is less than boundingRect.area
bool useSubdivision = summedArea < (0.7 * contentsRect.width() * contentsRect.height());
if ( !useSubdivision )
numRects = 1;
// iterate over the rects (only one loop if not using subdivision)
for ( uint i = 0; i < numRects; i++ )
{
if ( useSubdivision )
{
// set 'contentsRect' to a part of the sub-divided region
contentsRect = allRects[i].normalize().intersect( viewportRect );
contentsRect.moveBy( contentsX(), contentsY() );
if ( !contentsRect.isValid() )
continue;
}
// note: this check will take care of all things requiring alpha blending (not only selection)
bool wantCompositing = !selectionRect.isNull() && contentsRect.intersects( selectionRect );
if ( wantCompositing && KpdfSettings::enableCompositing() )
{
// create pixmap and open a painter over it (contents{left,top} becomes pixmap {0,0})
TQPixmap doubleBuffer( contentsRect.size() );
TQPainter pixmapPainter( &doubleBuffer );
pixmapPainter.translate( -contentsRect.left(), -contentsRect.top() );
// 1) Layer 0: paint items and clear bg on unpainted rects
paintItems( &pixmapPainter, contentsRect );
// 2) Layer 1: pixmap manipulated areas
// 3) Layer 2: paint (blend) transparent selection
if ( !selectionRect.isNull() && selectionRect.intersects( contentsRect ) &&
!selectionRectInternal.contains( contentsRect ) )
{
TQRect blendRect = selectionRectInternal.intersect( contentsRect );
// skip rectangles covered by the selection's border
if ( blendRect.isValid() )
{
// grab current pixmap into a new one to colorize contents
TQPixmap blendedPixmap( blendRect.width(), blendRect.height() );
copyBlt( &blendedPixmap, 0,0, &doubleBuffer,
blendRect.left() - contentsRect.left(), blendRect.top() - contentsRect.top(),
blendRect.width(), blendRect.height() );
// blend selBlendColor into the background pixmap
TQImage blendedImage = blendedPixmap.convertToImage();
KImageEffect::blend( selBlendColor.dark(140), blendedImage, 0.2 );
// copy the blended pixmap back to its place
pixmapPainter.drawPixmap( blendRect.left(), blendRect.top(), blendedImage );
}
// draw border (red if the selection is too small)
pixmapPainter.setPen( selBlendColor );
pixmapPainter.drawRect( selectionRect );
}
// 4) Layer 3: overlays
if ( KpdfSettings::debugDrawBoundaries() )
{
pixmapPainter.setPen( TQt::blue );
pixmapPainter.drawRect( contentsRect );
}
// finish painting and draw contents
pixmapPainter.end();
screenPainter.drawPixmap( contentsRect.left(), contentsRect.top(), doubleBuffer );
}
else
{
// 1) Layer 0: paint items and clear bg on unpainted rects
paintItems( &screenPainter, contentsRect );
// 2) Layer 1: opaque manipulated ares (filled / contours)
// 3) Layer 2: paint opaque selection
if ( !selectionRect.isNull() && selectionRect.intersects( contentsRect ) &&
!selectionRectInternal.contains( contentsRect ) )
{
screenPainter.setPen( palette().active().highlight().dark(110) );
screenPainter.drawRect( selectionRect );
}
// 4) Layer 3: overlays
if ( KpdfSettings::debugDrawBoundaries() )
{
screenPainter.setPen( TQt::red );
screenPainter.drawRect( contentsRect );
}
}
}
}
void PageView::viewportResizeEvent( TQResizeEvent * )
{
// start a timer that will refresh the pixmap after 0.5s
if ( !d->delayResizeTimer )
{
d->delayResizeTimer = new TQTimer( this );
connect( d->delayResizeTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( slotRelayoutPages() ) );
}
d->delayResizeTimer->start( 333, true );
}
void PageView::keyPressEvent( TQKeyEvent * e )
{
e->accept();
// if performing a selection or dyn zooming, disable keys handling
if ( ( !d->mouseSelectionRect.isNull() && e->key() != TQt::Key_Escape ) || d->mouseMidStartY != -1 )
return;
// handle 'find as you type' (based on tdehtml/tdehtmlview.cpp)
if( d->typeAheadActive )
{
// backspace: remove a char and search or terminates search
if( e->key() == Key_BackSpace )
{
if( d->typeAheadString.length() > 1 )
{
d->typeAheadString = d->typeAheadString.left( d->typeAheadString.length() - 1 );
bool found = d->document->searchText( PAGEVIEW_SEARCH_ID, d->typeAheadString, true, false,
KPDFDocument::NextMatch, true, tqRgb( 128, 255, 128 ), true );
TQString status = found ? i18n("Text found: \"%1\".") : i18n("Text not found: \"%1\".");
d->messageWindow->display( status.arg(d->typeAheadString.lower()),
found ? PageViewMessage::Find : PageViewMessage::Warning, 4000 );
d->findTimeoutTimer->start( 3000, true );
}
else
{
findAheadStop();
d->document->resetSearch( PAGEVIEW_SEARCH_ID );
}
}
// F3: go to next occurrency
else if( e->key() == TDEStdAccel::findNext() )
{
// part doesn't get this key event because of the keyboard grab
d->findTimeoutTimer->stop(); // restore normal operation during possible messagebox is displayed
// it is needed to grab the keyboard becase people may have Space assigned to a
// accel and without grabbing the keyboard you can not vim-search for space
// because it activates the accel
releaseKeyboard();
if ( d->document->continueSearch( PAGEVIEW_SEARCH_ID ) )
d->messageWindow->display( i18n("Text found: \"%1\".").arg(d->typeAheadString.lower()),
PageViewMessage::Find, 3000 );
d->findTimeoutTimer->start( 3000, true );
// it is needed to grab the keyboard becase people may have Space assigned to a
// accel and without grabbing the keyboard you can not vim-search for space
// because it activates the accel
grabKeyboard();
}
// esc and return: end search
else if( e->key() == Key_Escape || e->key() == Key_Return )
{
findAheadStop();
}
// other key: add to text and search
else if( !e->text().isEmpty() )
{
d->typeAheadString += e->text();
doTypeAheadSearch();
}
return;
}
else if( e->key() == '/' && d->document->isOpened() && d->document->supportsSearching() )
{
// stop scrolling the page (if doing it)
if ( d->autoScrollTimer )
{
d->scrollIncrement = 0;
d->autoScrollTimer->stop();
}
// start type-adeas search
d->typeAheadString = TQString();
d->messageWindow->display( i18n("Starting -- find text as you type"), PageViewMessage::Find, 3000 );
d->typeAheadActive = true;
if ( !d->findTimeoutTimer )
{
// create the timer on demand
d->findTimeoutTimer = new TQTimer( this );
connect( d->findTimeoutTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( findAheadStop() ) );
}
d->findTimeoutTimer->start( 3000, true );
// it is needed to grab the keyboard becase people may have Space assigned to a
// accel and without grabbing the keyboard you can not vim-search for space
// because it activates the accel
grabKeyboard();
return;
}
// if viewport is moving, disable keys handling
if ( d->viewportMoveActive )
return;
// move/scroll page by using keys
switch ( e->key() )
{
case Key_Up:
case Key_PageUp:
case Key_Backspace:
// if in single page mode and at the top of the screen, go to previous page
if ( KpdfSettings::viewContinuous() || verticalScrollBar()->value() > verticalScrollBar()->minValue() )
{
if ( e->key() == Key_Up )
verticalScrollBar()->subtractLine();
else
verticalScrollBar()->subtractPage();
}
else if ( d->document->currentPage() > 0 )
{
// more optimized than document->setPrevPage and then move view to bottom
DocumentViewport newViewport = d->document->viewport();
newViewport.pageNumber -= 1;
newViewport.rePos.enabled = true;
newViewport.rePos.normalizedY = 1.0;
d->document->setViewport( newViewport );
}
break;
case Key_Down:
case Key_PageDown:
case Key_Space:
// if in single page mode and at the bottom of the screen, go to next page
if ( KpdfSettings::viewContinuous() || verticalScrollBar()->value() < verticalScrollBar()->maxValue() )
{
if ( e->key() == Key_Down )
verticalScrollBar()->addLine();
else
verticalScrollBar()->addPage();
}
else if ( d->document->currentPage() < d->items.count() - 1 )
{
// more optmized than document->setNextPage and then move view to top
DocumentViewport newViewport = d->document->viewport();
newViewport.pageNumber += 1;
newViewport.rePos.enabled = true;
newViewport.rePos.normalizedY = 0.0;
d->document->setViewport( newViewport );
}
break;
case Key_Left:
horizontalScrollBar()->subtractLine();
break;
case Key_Right:
horizontalScrollBar()->addLine();
break;
case TQt::Key_Escape:
selectionClear();
d->mousePressPos = TQPoint();
if ( d->aPrevAction )
{
d->aPrevAction->activate();
d->aPrevAction = 0;
}
break;
case Key_Shift:
case Key_Control:
if ( d->autoScrollTimer )
{
if ( d->autoScrollTimer->isActive() )
d->autoScrollTimer->stop();
else
slotAutoScoll();
return;
}
// else fall trhough
default:
e->ignore();
return;
}
// if a known key has been pressed, stop scrolling the page
if ( d->autoScrollTimer )
{
d->scrollIncrement = 0;
d->autoScrollTimer->stop();
}
}
void PageView::imEndEvent( TQIMEvent * e )
{
if( d->typeAheadActive )
{
if( !e->text().isEmpty() )
{
d->typeAheadString += e->text();
doTypeAheadSearch();
e->accept();
}
}
}
void PageView::contentsMouseMoveEvent( TQMouseEvent * e )
{
// don't perform any mouse action when no document is shown
if ( d->items.isEmpty() )
return;
// don't perform any mouse action when viewport is autoscrolling
if ( d->viewportMoveActive )
return;
// if holding mouse mid button, perform zoom
if ( (e->state() & Qt::MidButton) && d->mouseMidStartY >= 0 )
{
int deltaY = d->mouseMidStartY - e->globalPos().y();
d->mouseMidStartY = e->globalPos().y();
d->zoomFactor *= ( 1.0 + ( (double)deltaY / 500.0 ) );
updateZoom( ZoomRefreshCurrent );
// uncomment following line to force a complete redraw
viewport()->repaint( false );
return;
}
bool leftButton = e->state() & Qt::LeftButton,
rightButton = e->state() & Qt::RightButton;
switch ( d->mouseMode )
{
case MouseNormal:
if ( leftButton )
{
// drag page
if ( !d->mouseGrabPos.isNull() )
{
// scroll page by position increment
TQPoint delta = d->mouseGrabPos - e->globalPos();
scrollBy( delta.x(), delta.y() );
d->mouseGrabPos = e->globalPos();
}
}
else if ( rightButton && !d->mousePressPos.isNull() )
{
// if mouse moves 5 px away from the press point, switch to 'selection'
int deltaX = d->mousePressPos.x() - e->globalPos().x(),
deltaY = d->mousePressPos.y() - e->globalPos().y();
if ( deltaX > 5 || deltaX < -5 || deltaY > 5 || deltaY < -5 )
{
d->aPrevAction = d->aMouseNormal;
d->aMouseSelect->activate();
TQColor selColor = palette().active().highlight().light( 120 );
selectionStart( e->x() + deltaX, e->y() + deltaY, selColor, false );
selectionEndPoint( e->x(), e->y() );
break;
}
}
else
{
// only hovering the page, so update the cursor
updateCursor( e->pos() );
}
break;
case MouseZoom:
case MouseSelect:
// set second corner of selection
if ( !d->mousePressPos.isNull() && ( leftButton || d->aPrevAction ) )
selectionEndPoint( e->x(), e->y() );
break;
case MouseEdit: // ? update graphics ?
break;
}
}
void PageView::contentsMousePressEvent( TQMouseEvent * e )
{
// don't perform any mouse action when no document is shown
if ( d->items.isEmpty() )
return;
// if performing a selection or dyn zooming, disable mouse press
if ( !d->mouseSelectionRect.isNull() || d->mouseMidStartY != -1 ||
d->viewportMoveActive )
return;
// if the page is scrolling, stop it
if ( d->autoScrollTimer )
{
d->scrollIncrement = 0;
d->autoScrollTimer->stop();
}
// if pressing mid mouse button while not doing other things, begin 'comtinous zoom' mode
if ( e->button() & Qt::MidButton )
{
d->mouseMidStartY = e->globalPos().y();
setCursor( KCursor::sizeVerCursor() );
return;
}
// update press / 'start drag' mouse position
d->mousePressPos = e->globalPos();
// handle mode dependant mouse press actions
bool leftButton = e->button() & Qt::LeftButton,
rightButton = e->button() & Qt::RightButton;
switch ( d->mouseMode )
{
case MouseNormal: // drag start / click / link following
if ( leftButton )
{
d->mouseGrabPos = d->mouseOnRect ? TQPoint() : d->mousePressPos;
if ( !d->mouseOnRect )
setCursor( KCursor::sizeAllCursor() );
}
break;
case MouseZoom: // set first corner of the zoom rect
if ( leftButton )
selectionStart( e->x(), e->y(), palette().active().highlight(), false );
else if ( rightButton )
updateZoom( ZoomOut );
break;
case MouseSelect: // set first corner of the selection rect
if ( leftButton )
{
TQColor selColor = palette().active().highlight().light( 120 );
selectionStart( e->x(), e->y(), selColor, false );
}
break;
case MouseEdit: // ..to do..
break;
}
}
void PageView::contentsMouseReleaseEvent( TQMouseEvent * e )
{
// stop the drag scrolling
d->dragScrollTimer.stop();
// don't perform any mouse action when no document is shown
if ( d->items.isEmpty() )
{
// ..except for right Clicks (emitted even it viewport is empty)
if ( e->button() == Qt::RightButton )
emit rightClick( 0, e->globalPos() );
return;
}
// don't perform any mouse action when viewport is autoscrolling
if ( d->viewportMoveActive )
return;
// handle mode indepent mid buttom zoom
bool midButton = e->button() & Qt::MidButton;
if ( midButton && d->mouseMidStartY > 0 )
{
d->mouseMidStartY = -1;
// while drag-zooming we could have gone over a link
updateCursor( e->pos() );
return;
}
bool leftButton = e->button() & Qt::LeftButton,
rightButton = e->button() & Qt::RightButton;
switch ( d->mouseMode )
{
case MouseNormal:{
// return the cursor to its normal state after dragging
if ( cursor().shape() == TQt::SizeAllCursor )
updateCursor( e->pos() );
PageViewItem * pageItem = pickItemOnPoint( e->x(), e->y() );
// if the mouse has not moved since the press, that's a -click-
if ( leftButton && pageItem && d->mousePressPos == e->globalPos())
{
double nX = (double)(e->x() - pageItem->geometry().left()) / (double)pageItem->width(),
nY = (double)(e->y() - pageItem->geometry().top()) / (double)pageItem->height();
const ObjectRect * linkRect, * imageRect;
linkRect = pageItem->page()->hasObject( ObjectRect::Link, nX, nY );
if ( linkRect )
{
// handle click over a link
const KPDFLink * link = static_cast< const KPDFLink * >( linkRect->pointer() );
d->document->processLink( link );
}
else
{
// a link can move us to another page or even to another document, there's no point in trying to process the click on the image once we have processes the click on the link
imageRect = pageItem->page()->hasObject( ObjectRect::Image, nX, nY );
if ( imageRect )
{
// handle click over a image
}
// Enrico and me have decided this is not worth the trouble it generates
// else
// {
// if not on a rect, the click selects the page
// d->document->setViewportPage( pageItem->pageNumber(), PAGEVIEW_ID );
// }
}
}
else if ( rightButton )
{
// right click (if not within 5 px of the press point, the mode
// had been already changed to 'Selection' instead of 'Normal')
emit rightClick( pageItem ? pageItem->page() : 0, e->globalPos() );
}
}break;
case MouseZoom:
// if a selection rect has been defined, zoom into it
if ( leftButton && !d->mouseSelectionRect.isNull() )
{
TQRect selRect = d->mouseSelectionRect.normalize();
if ( selRect.width() <= 8 && selRect.height() <= 8 )
{
selectionClear();
break;
}
// find out new zoom ratio and normalized view center (relative to the contentsRect)
double zoom = TQMIN( (double)visibleWidth() / (double)selRect.width(), (double)visibleHeight() / (double)selRect.height() );
double nX = (double)(selRect.left() + selRect.right()) / (2.0 * (double)contentsWidth());
double nY = (double)(selRect.top() + selRect.bottom()) / (2.0 * (double)contentsHeight());
// zoom up to 400%
if ( d->zoomFactor <= 4.0 || zoom <= 1.0 )
{
d->zoomFactor *= zoom;
viewport()->setUpdatesEnabled( false );
updateZoom( ZoomRefreshCurrent );
viewport()->setUpdatesEnabled( true );
}
// recenter view and update the viewport
center( (int)(nX * contentsWidth()), (int)(nY * contentsHeight()) );
updateContents();
// hide message box and delete overlay window
selectionClear();
}
break;
case MouseSelect:{
if (d->mouseSelectionRect.isNull() && rightButton)
{
PageViewItem * pageItem = pickItemOnPoint( e->x(), e->y() );
emit rightClick( pageItem ? pageItem->page() : 0, e->globalPos() );
}
// if a selection is defined, display a popup
if ( (!leftButton && !d->aPrevAction) || (leftButton && d->aPrevAction) ||
d->mouseSelectionRect.isNull() )
break;
TQRect selectionRect = d->mouseSelectionRect.normalize();
if ( selectionRect.width() <= 8 && selectionRect.height() <= 8 )
{
selectionClear();
if ( d->aPrevAction )
{
d->aPrevAction->activate();
d->aPrevAction = 0;
}
break;
}
// grab text in selection by extracting it from all intersected pages
TQString selectedText;
TQValueVector< PageViewItem * >::iterator iIt = d->items.begin(), iEnd = d->items.end();
for ( ; iIt != iEnd; ++iIt )
{
PageViewItem * item = *iIt;
const TQRect & itemRect = item->geometry();
if ( selectionRect.intersects( itemRect ) )
{
// request the textpage if there isn't one
const KPDFPage * kpdfPage = item->page();
if ( !kpdfPage->hasSearchPage() )
d->document->requestTextPage( kpdfPage->number() );
// grab text in the rect that intersects itemRect
TQRect relativeRect = selectionRect.intersect( itemRect );
relativeRect.moveBy( -itemRect.left(), -itemRect.top() );
NormalizedRect normRect( relativeRect, item->width(), item->height() );
selectedText += kpdfPage->getText( normRect );
}
}
// popup that ask to copy:text and copy/save:image
TDEPopupMenu menu( this );
if ( !selectedText.isEmpty() )
{
menu.insertTitle( i18n( "Text (1 character)", "Text (%n characters)", selectedText.length() ) );
menu.insertItem( SmallIcon("edit-copy"), i18n( "Copy to Clipboard" ), 1 );
if ( !d->document->isAllowed( KPDFDocument::AllowCopy ) )
menu.setItemEnabled( 1, false );
if ( KpdfSettings::useKTTSD() )
menu.insertItem( SmallIcon("kttsd"), i18n( "Speak Text" ), 2 );
}
menu.insertTitle( i18n( "Image (%1 by %2 pixels)" ).arg( selectionRect.width() ).arg( selectionRect.height() ) );
menu.insertItem( SmallIcon("image-x-generic"), i18n( "Copy to Clipboard" ), 3 );
menu.insertItem( SmallIcon("document-save"), i18n( "Save to File..." ), 4 );
int choice = menu.exec( e->globalPos() );
// IMAGE operation choosen
if ( choice > 2 )
{
// renders page into a pixmap
TQPixmap copyPix( selectionRect.width(), selectionRect.height() );
TQPainter copyPainter( &copyPix );
copyPainter.translate( -selectionRect.left(), -selectionRect.top() );
paintItems( &copyPainter, selectionRect );
if ( choice == 3 )
{
// [2] copy pixmap to clipboard
TQClipboard *cb = TQApplication::clipboard();
cb->setPixmap( copyPix, TQClipboard::Clipboard );
if ( cb->supportsSelection() )
cb->setPixmap( copyPix, TQClipboard::Selection );
d->messageWindow->display( i18n( "Image [%1x%2] copied to clipboard." ).arg( copyPix.width() ).arg( copyPix.height() ) );
}
else if ( choice == 4 )
{
// [3] save pixmap to file
TQString fileName = KFileDialog::getSaveFileName( TQString(), "image/png image/jpeg", this );
if ( fileName.isNull() )
d->messageWindow->display( i18n( "File not saved." ), PageViewMessage::Warning );
else
{
TQString type( KImageIO::type( fileName ) );
if ( type.isNull() )
type = "PNG";
copyPix.save( fileName, type.latin1() );
d->messageWindow->display( i18n( "Image [%1x%2] saved to %3 file." ).arg( copyPix.width() ).arg( copyPix.height() ).arg( type ) );
}
}
}
// TEXT operation choosen
else
{
if ( choice == 1 )
{
// [1] copy text to clipboard
TQClipboard *cb = TQApplication::clipboard();
cb->setText( selectedText, TQClipboard::Clipboard );
if ( cb->supportsSelection() )
cb->setText( selectedText, TQClipboard::Selection );
}
else if ( choice == 2 )
{
// [2] speech selection using KTTSD
DCOPClient * client = DCOPClient::mainClient();
// Albert says is this ever necessary?
// we already attached on Part constructor
// if ( !client->isAttached() )
// client->attach();
// If KTTSD not running, start it.
if (!client->isApplicationRegistered("kttsd"))
{
TQString error;
if (TDEApplication::startServiceByDesktopName("kttsd", TQStringList(), &error))
{
d->messageWindow->display( i18n("Starting KTTSD Failed: %1").arg(error) );
KpdfSettings::setUseKTTSD(false);
KpdfSettings::writeConfig();
}
}
if ( KpdfSettings::useKTTSD() )
{
// serialize the text to speech (selectedText) and the
// preferred reader ("" is the default voice) ...
TQByteArray data;
TQDataStream arg( data, IO_WriteOnly );
arg << selectedText;
arg << TQString();
TQCString replyType;
TQByteArray replyData;
// ..and send it to KTTSD
if (client->call( "kttsd", "KSpeech", "setText(TQString,TQString)", data, replyType, replyData, true ))
{
TQByteArray data2;
TQDataStream arg2(data2, IO_WriteOnly);
arg2 << 0;
client->send("kttsd", "KSpeech", "startText(uint)", data2 );
}
}
}
}
// clear widget selection and invalidate rect
selectionClear();
// restore previous action if came from it using right button
if ( d->aPrevAction )
{
d->aPrevAction->activate();
d->aPrevAction = 0;
}
}break;
case MouseEdit: // ? apply [tool] ?
break;
}
// reset mouse press / 'drag start' position
d->mousePressPos = TQPoint();
}
void PageView::wheelEvent( TQWheelEvent *e )
{
// don't perform any mouse action when viewport is autoscrolling
if ( d->viewportMoveActive )
return;
if ( !d->document->isOpened() )
{
TQScrollView::wheelEvent( e );
return;
}
int delta = e->delta(),
vScroll = verticalScrollBar()->value();
e->accept();
if ( (e->state() & ControlButton) == ControlButton ) {
if ( e->delta() < 0 )
slotZoomOut();
else
slotZoomIn();
}
else if ( delta <= -120 && !KpdfSettings::viewContinuous() && vScroll == verticalScrollBar()->maxValue() )
{
// go to next page
if ( d->document->currentPage() < d->items.count() - 1 )
{
// more optmized than document->setNextPage and then move view to top
DocumentViewport newViewport = d->document->viewport();
newViewport.pageNumber += 1;
newViewport.rePos.enabled = true;
newViewport.rePos.normalizedY = 0.0;
d->document->setViewport( newViewport );
}
}
else if ( delta >= 120 && !KpdfSettings::viewContinuous() && vScroll == verticalScrollBar()->minValue() )
{
// go to prev page
if ( d->document->currentPage() > 0 )
{
// more optmized than document->setPrevPage and then move view to bottom
DocumentViewport newViewport = d->document->viewport();
newViewport.pageNumber -= 1;
newViewport.rePos.enabled = true;
newViewport.rePos.normalizedY = 1.0;
d->document->setViewport( newViewport );
}
}
else
TQScrollView::wheelEvent( e );
TQPoint cp = viewportToContents(e->pos());
updateCursor(cp);
}
void PageView::dragEnterEvent( TQDragEnterEvent * ev )
{
ev->accept();
}
void PageView::dropEvent( TQDropEvent * ev )
{
KURL::List lst;
if ( KURLDrag::decode( ev, lst ) )
emit urlDropped( lst.first() );
}
//END widget events
void PageView::paintItems( TQPainter * p, const TQRect & contentsRect )
{
// when checking if an Item is contained in contentsRect, instead of
// growing PageViewItems rects (for keeping outline into account), we
// grow the contentsRect
TQRect checkRect = contentsRect;
checkRect.addCoords( -3, -3, 1, 1 );
// create a region from wich we'll subtract painted rects
TQRegion remainingArea( contentsRect );
//TQValueVector< PageViewItem * >::iterator iIt = d->visibleItems.begin(), iEnd = d->visibleItems.end();
TQValueVector< PageViewItem * >::iterator iIt = d->items.begin(), iEnd = d->items.end();
for ( ; iIt != iEnd; ++iIt )
{
// check if a piece of the page intersects the contents rect
if ( !(*iIt)->geometry().intersects( checkRect ) )
continue;
PageViewItem * item = *iIt;
TQRect pixmapGeometry = item->geometry();
// translate the painter so we draw top-left pixmap corner in 0,0
p->save();
p->translate( pixmapGeometry.left(), pixmapGeometry.top() );
// item pixmap and outline geometry
TQRect outlineGeometry = pixmapGeometry;
outlineGeometry.addCoords( -1, -1, 3, 3 );
// draw the page outline (little black border and 2px shadow)
if ( !pixmapGeometry.contains( contentsRect ) )
{
int pixmapWidth = pixmapGeometry.width(),
pixmapHeight = pixmapGeometry.height();
// draw simple outline
p->setPen( TQt::black );
p->drawRect( -1, -1, pixmapWidth + 2, pixmapHeight + 2 );
// draw bottom/right gradient
int levels = 2;
int r = TQt::gray.red() / (levels + 2),
g = TQt::gray.green() / (levels + 2),
b = TQt::gray.blue() / (levels + 2);
for ( int i = 0; i < levels; i++ )
{
p->setPen( TQColor( r * (i+2), g * (i+2), b * (i+2) ) );
p->drawLine( i, i + pixmapHeight + 1, i + pixmapWidth + 1, i + pixmapHeight + 1 );
p->drawLine( i + pixmapWidth + 1, i, i + pixmapWidth + 1, i + pixmapHeight );
p->setPen( TQt::gray );
p->drawLine( -1, i + pixmapHeight + 1, i - 1, i + pixmapHeight + 1 );
p->drawLine( i + pixmapWidth + 1, -1, i + pixmapWidth + 1, i - 1 );
}
}
// draw the pixmap (note: this modifies the painter)
if ( contentsRect.intersects( pixmapGeometry ) )
{
TQRect pixmapRect = contentsRect.intersect( pixmapGeometry );
pixmapRect.moveBy( -pixmapGeometry.left(), -pixmapGeometry.top() );
int flags = PagePainter::Accessibility | PagePainter::EnhanceLinks |
PagePainter::EnhanceImages | PagePainter::Highlights;
PagePainter::paintPageOnPainter( item->page(), PAGEVIEW_ID, flags, p, pixmapRect,
pixmapGeometry.width(), pixmapGeometry.height() );
}
// remove painted area from 'remainingArea' and restore painter
remainingArea -= outlineGeometry.intersect( contentsRect );
p->restore();
}
// paint with background color the unpainted area
TQMemArray<TQRect> backRects = remainingArea.rects();
uint backRectsNumber = backRects.count();
for ( uint jr = 0; jr < backRectsNumber; jr++ )
p->fillRect( backRects[ jr ], TQt::gray );
}
void PageView::updateItemSize( PageViewItem * item, int colWidth, int rowHeight )
{
const KPDFPage * kpdfPage = item->page();
double width = kpdfPage->width(),
height = kpdfPage->height(),
zoom = d->zoomFactor;
if ( d->zoomMode == ZoomFixed )
{
width *= zoom;
height *= zoom;
item->setWHZ( (int)width, (int)height, d->zoomFactor );
}
else if ( d->zoomMode == ZoomFitWidth )
{
height = kpdfPage->ratio() * colWidth;
item->setWHZ( colWidth, (int)height, (double)colWidth / width );
d->zoomFactor = (double)colWidth / width;
}
else if ( d->zoomMode == ZoomFitPage )
{
double scaleW = (double)colWidth / (double)width;
double scaleH = (double)rowHeight / (double)height;
zoom = TQMIN( scaleW, scaleH );
item->setWHZ( (int)(zoom * width), (int)(zoom * height), zoom );
d->zoomFactor = zoom;
}
#ifndef NDEBUG
else
kdDebug() << "calling updateItemSize with unrecognized d->zoomMode!" << endl;
#endif
}
PageViewItem * PageView::pickItemOnPoint( int x, int y )
{
PageViewItem * item = 0;
TQValueList< PageViewItem * >::iterator iIt = d->visibleItems.begin(), iEnd = d->visibleItems.end();
for ( ; iIt != iEnd; ++iIt )
{
PageViewItem * i = *iIt;
const TQRect & r = i->geometry();
if ( x < r.right() && x > r.left() && y < r.bottom() )
{
if ( y > r.top() )
item = i;
break;
}
}
return item;
}
void PageView::selectionStart( int x, int y, const TQColor & color, bool /*aboveAll*/ )
{
d->mouseSelectionRect.setRect( x, y, 1, 1 );
d->selectionRectColor = color;
// ensures page doesn't scroll
if ( d->autoScrollTimer )
{
d->scrollIncrement = 0;
d->autoScrollTimer->stop();
}
}
void PageView::selectionEndPoint( int x, int y )
{
if (x < contentsX()) d->dragScrollVector.setX(x - contentsX());
else if (contentsX() + viewport()->width() < x) d->dragScrollVector.setX(x - contentsX() - viewport()->width());
else d->dragScrollVector.setX(0);
if (y < contentsY()) d->dragScrollVector.setY(y - contentsY());
else if (contentsY() + viewport()->height() < y) d->dragScrollVector.setY(y - contentsY() - viewport()->height());
else d->dragScrollVector.setY(0);
if (d->dragScrollVector != TQPoint(0, 0))
{
if (!d->dragScrollTimer.isActive()) d->dragScrollTimer.start(100);
}
else d->dragScrollTimer.stop();
// clip selection to the viewport
TQRect viewportRect( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
x = TQMAX( TQMIN( x, viewportRect.right() ), viewportRect.left() );
y = TQMAX( TQMIN( y, viewportRect.bottom() ), viewportRect.top() );
// if selection changed update rect
if ( d->mouseSelectionRect.right() != x || d->mouseSelectionRect.bottom() != y )
{
// send incremental paint events
TQRect oldRect = d->mouseSelectionRect.normalize();
d->mouseSelectionRect.setRight( x );
d->mouseSelectionRect.setBottom( y );
TQRect newRect = d->mouseSelectionRect.normalize();
// generate diff region: [ OLD.unite(NEW) - OLD.intersect(NEW) ]
TQRegion compoundRegion = TQRegion( oldRect ).unite( newRect );
if ( oldRect.intersects( newRect ) )
{
TQRect intersection = oldRect.intersect( newRect );
intersection.addCoords( 1, 1, -1, -1 );
if ( intersection.width() > 20 && intersection.height() > 20 )
compoundRegion -= intersection;
}
// tassellate region with rects and enqueue paint events
TQMemArray<TQRect> rects = compoundRegion.rects();
for ( uint i = 0; i < rects.count(); i++ )
updateContents( rects[i] );
}
}
void PageView::selectionClear()
{
updateContents( d->mouseSelectionRect.normalize() );
d->mouseSelectionRect.setCoords( 0, 0, -1, -1 );
}
void PageView::updateZoom( ZoomMode newZoomMode )
{
if ( newZoomMode == ZoomFixed )
{
if ( d->aZoom->currentItem() == 0 )
newZoomMode = ZoomFitWidth;
else if ( d->aZoom->currentItem() == 1 )
newZoomMode = ZoomFitPage;
}
float newFactor = d->zoomFactor;
TDEAction * checkedZoomAction = 0;
switch ( newZoomMode )
{
case ZoomFixed:{ //ZoomFixed case
TQString z = d->aZoom->currentText();
newFactor = TDEGlobal::locale()->readNumber( z.remove( z.find( '%' ), 1 ) ) / 100.0;
}break;
case ZoomIn:
newFactor += (newFactor > 0.99) ? ( newFactor > 1.99 ? 0.5 : 0.2 ) : 0.1;
newZoomMode = ZoomFixed;
break;
case ZoomOut:
newFactor -= (newFactor > 0.99) ? ( newFactor > 1.99 ? 0.5 : 0.2 ) : 0.1;
newZoomMode = ZoomFixed;
break;
case ZoomFitWidth:
checkedZoomAction = d->aZoomFitWidth;
break;
case ZoomFitPage:
checkedZoomAction = d->aZoomFitPage;
break;
case ZoomFitText:
checkedZoomAction = d->aZoomFitText;
break;
case ZoomRefreshCurrent:
newZoomMode = ZoomFixed;
d->zoomFactor = -1;
break;
}
if ( newFactor > 4.0 )
newFactor = 4.0;
if ( newFactor < 0.1 )
newFactor = 0.1;
if ( newZoomMode != d->zoomMode || (newZoomMode == ZoomFixed && newFactor != d->zoomFactor ) )
{
// rebuild layout and update the whole viewport
d->zoomMode = newZoomMode;
d->zoomFactor = newFactor;
// be sure to block updates to document's viewport
bool prevState = d->blockViewport;
d->blockViewport = true;
slotRelayoutPages();
d->blockViewport = prevState;
// request pixmaps
slotRequestVisiblePixmaps();
// update zoom text
updateZoomText();
// update actions checked state
d->aZoomFitWidth->setChecked( checkedZoomAction == d->aZoomFitWidth );
d->aZoomFitPage->setChecked( checkedZoomAction == d->aZoomFitPage );
d->aZoomFitText->setChecked( checkedZoomAction == d->aZoomFitText );
// save selected zoom factor
KpdfSettings::setZoomMode(newZoomMode);
KpdfSettings::setZoomFactor(newFactor);
KpdfSettings::writeConfig();
}
}
void PageView::updateZoomText()
{
// use current page zoom as zoomFactor if in ZoomFit/* mode
if ( d->zoomMode != ZoomFixed && d->items.count() > 0 )
d->zoomFactor = d->items[ TQMAX( 0, (int)d->document->currentPage() ) ]->zoomFactor();
float newFactor = d->zoomFactor;
d->aZoom->clear();
// add items that describe fit actions
TQStringList translated;
translated << i18n("Fit Width") << i18n("Fit Page"); // << i18n("Fit Text");
// add percent items
TQString double_oh( "00" );
const float zoomValue[10] = { 0.125, 0.25, 0.333, 0.5, 0.667, 0.75, 1, 1.25, 1.50, 2 };
int idx = 0,
selIdx = 2; // use 3 if "fit text" present
bool inserted = false; //use: "d->zoomMode != ZoomFixed" to hide Fit/* zoom ratio
while ( idx < 10 || !inserted )
{
float value = idx < 10 ? zoomValue[ idx ] : newFactor;
if ( !inserted && newFactor < (value - 0.0001) )
value = newFactor;
else
idx ++;
if ( value > (newFactor - 0.0001) && value < (newFactor + 0.0001) )
inserted = true;
if ( !inserted )
selIdx++;
TQString localValue( TDEGlobal::locale()->formatNumber( value * 100.0, 2 ) );
localValue.remove( TDEGlobal::locale()->decimalSymbol() + double_oh );
translated << TQString( "%1%" ).arg( localValue );
}
d->aZoom->setItems( translated );
// select current item in list
if ( d->zoomMode == ZoomFitWidth )
selIdx = 0;
else if ( d->zoomMode == ZoomFitPage )
selIdx = 1;
else if ( d->zoomMode == ZoomFitText )
selIdx = 2;
d->aZoom->setCurrentItem( selIdx );
}
void PageView::updateCursor( const TQPoint &p )
{
// detect the underlaying page (if present)
PageViewItem * pageItem = pickItemOnPoint( p.x(), p.y() );
if ( pageItem && d->mouseMode == MouseNormal )
{
double nX = (double)(p.x() - pageItem->geometry().left()) / (double)pageItem->width(),
nY = (double)(p.y() - pageItem->geometry().top()) / (double)pageItem->height();
// if over a ObjectRect (of type Link) change cursor to hand
d->mouseOnRect = pageItem->page()->hasObject( ObjectRect::Link, nX, nY );
if ( d->mouseOnRect )
setCursor( KCursor::handCursor() );
else
setCursor( KCursor::arrowCursor() );
}
else
{
// if there's no page over the cursor and we were showing the pointingHandCursor
// go back to the normal one
d->mouseOnRect = false;
setCursor( KCursor::arrowCursor() );
}
}
void PageView::doTypeAheadSearch()
{
bool found = d->document->searchText( PAGEVIEW_SEARCH_ID, d->typeAheadString, false, false,
KPDFDocument::NextMatch, true, tqRgb( 128, 255, 128 ), true );
TQString status = found ? i18n("Text found: \"%1\".") : i18n("Text not found: \"%1\".");
d->messageWindow->display( status.arg(d->typeAheadString.lower()),
found ? PageViewMessage::Find : PageViewMessage::Warning, 4000 );
d->findTimeoutTimer->start( 3000, true );
}
//BEGIN private SLOTS
void PageView::slotRelayoutPages()
// called by: notifySetup, viewportResizeEvent, slotTwoPagesToggled, slotContinuousToggled, updateZoom
{
// set an empty container if we have no pages
int pageCount = d->items.count();
if ( pageCount < 1 )
{
resizeContents( 0, 0 );
return;
}
// if viewport was auto-moving, stop it
if ( d->viewportMoveActive )
{
d->viewportMoveActive = false;
d->viewportMoveTimer->stop();
verticalScrollBar()->setEnabled( true );
horizontalScrollBar()->setEnabled( true );
}
// common iterator used in this method and viewport parameters
TQValueVector< PageViewItem * >::iterator iIt, iEnd = d->items.end();
int viewportWidth = visibleWidth(),
viewportHeight = visibleHeight(),
fullWidth = 0,
fullHeight = 0;
TQRect viewportRect( contentsX(), contentsY(), viewportWidth, viewportHeight );
// set all items geometry and resize contents. handle 'continuous' and 'single' modes separately
if ( KpdfSettings::viewContinuous() )
{
// Here we find out column's width and row's height to compute a table
// so we can place widgets 'centered in virtual cells'.
int nCols = KpdfSettings::viewColumns(),
nRows = (int)ceil( (float)pageCount / (float)nCols ),
* colWidth = new int[ nCols ],
* rowHeight = new int[ nRows ],
cIdx = 0,
rIdx = 0;
for ( int i = 0; i < nCols; i++ )
colWidth[ i ] = viewportWidth / nCols;
for ( int i = 0; i < nRows; i++ )
rowHeight[ i ] = 0;
// 1) find the maximum columns width and rows height for a grid in
// which each page must well-fit inside a cell
for ( iIt = d->items.begin(); iIt != iEnd; ++iIt )
{
PageViewItem * item = *iIt;
// update internal page size (leaving a little margin in case of Fit* modes)
updateItemSize( item, colWidth[ cIdx ] - 6, viewportHeight - 8 );
// find row's maximum height and column's max width
if ( item->width() + 6 > colWidth[ cIdx ] )
colWidth[ cIdx ] = item->width() + 6;
if ( item->height() > rowHeight[ rIdx ] )
rowHeight[ rIdx ] = item->height();
// update col/row indices
if ( ++cIdx == nCols )
{
cIdx = 0;
rIdx++;
}
}
// 2) arrange widgets inside cells
int insertX = 0,
insertY = 4; // 2 + 4*d->zoomFactor ?
cIdx = 0;
rIdx = 0;
for ( iIt = d->items.begin(); iIt != iEnd; ++iIt )
{
PageViewItem * item = *iIt;
int cWidth = colWidth[ cIdx ],
rHeight = rowHeight[ rIdx ];
// center widget inside 'cells'
item->moveTo( insertX + (cWidth - item->width()) / 2,
insertY + (rHeight - item->height()) / 2 );
// advance col/row index
insertX += cWidth;
if ( ++cIdx == nCols )
{
cIdx = 0;
rIdx++;
insertX = 0;
insertY += rHeight + 15; // 5 + 15*d->zoomFactor ?
}
}
fullHeight = cIdx ? (insertY + rowHeight[ rIdx ] + 10) : insertY;
for ( int i = 0; i < nCols; i++ )
fullWidth += colWidth[ i ];
delete [] colWidth;
delete [] rowHeight;
}
else // viewContinuous is FALSE
{
PageViewItem * currentItem = d->items[ TQMAX( 0, (int)d->document->currentPage() ) ];
// setup varialbles for a 1(row) x N(columns) grid
int nCols = KpdfSettings::viewColumns(),
* colWidth = new int[ nCols ],
cIdx = 0;
fullHeight = viewportHeight;
for ( int i = 0; i < nCols; i++ )
colWidth[ i ] = viewportWidth / nCols;
// 1) find out maximum area extension for the pages
for ( iIt = d->items.begin(); iIt != iEnd; ++iIt )
{
PageViewItem * item = *iIt;
if ( item == currentItem || (cIdx > 0 && cIdx < nCols) )
{
// update internal page size (leaving a little margin in case of Fit* modes)
updateItemSize( item, colWidth[ cIdx ] - 6, viewportHeight - 8 );
// find row's maximum height and column's max width
if ( item->width() + 6 > colWidth[ cIdx ] )
colWidth[ cIdx ] = item->width() + 6;
if ( item->height() + 8 > fullHeight )
fullHeight = item->height() + 8;
cIdx++;
}
}
// 2) hide all widgets except the displayable ones and dispose those
int insertX = 0;
cIdx = 0;
for ( iIt = d->items.begin(); iIt != iEnd; ++iIt )
{
PageViewItem * item = *iIt;
if ( item == currentItem || (cIdx > 0 && cIdx < nCols) )
{
// center widget inside 'cells'
item->moveTo( insertX + (colWidth[ cIdx ] - item->width()) / 2,
(fullHeight - item->height()) / 2 );
// advance col index
insertX += colWidth[ cIdx ];
cIdx++;
} else
item->setGeometry( 0, 0, -1, -1 );
}
for ( int i = 0; i < nCols; i++ )
fullWidth += colWidth[ i ];
delete [] colWidth;
}
// 3) reset dirty state
d->dirtyLayout = false;
// 4) update scrollview's contents size and recenter view
bool wasUpdatesEnabled = viewport()->isUpdatesEnabled();
if ( fullWidth != contentsWidth() || fullHeight != contentsHeight() )
{
// disable updates and resize the viewportContents
if ( wasUpdatesEnabled )
viewport()->setUpdatesEnabled( false );
resizeContents( fullWidth, fullHeight );
// restore previous viewport if defined and updates enabled
if ( wasUpdatesEnabled )
{
const DocumentViewport & vp = d->document->viewport();
if ( vp.pageNumber >= 0 )
{
int prevX = contentsX(),
prevY = contentsY();
const TQRect & geometry = d->items[ vp.pageNumber ]->geometry();
double nX = vp.rePos.enabled ? vp.rePos.normalizedX : 0.5,
nY = vp.rePos.enabled ? vp.rePos.normalizedY : 0.0;
center( geometry.left() + ROUND( nX * (double)geometry.width() ),
geometry.top() + ROUND( nY * (double)geometry.height() ) );
// center() usually moves the viewport, that requests pixmaps too.
// if that doesn't happen we have to request them by hand
if ( prevX == contentsX() && prevY == contentsY() )
slotRequestVisiblePixmaps();
}
// or else go to center page
else
center( fullWidth / 2, 0 );
viewport()->setUpdatesEnabled( true );
}
}
// 5) update the whole viewport if updated enabled
if ( wasUpdatesEnabled )
updateContents();
}
void PageView::slotRequestVisiblePixmaps( int newLeft, int newTop )
{
// if requests are blocked (because raised by an unwanted event), exit
if ( d->blockPixmapsRequest || d->viewportMoveActive )
return;
// precalc view limits for intersecting with page coords inside the lOOp
bool isEvent = newLeft != -1 && newTop != -1 && !d->blockViewport;
TQRect viewportRect( isEvent ? newLeft : contentsX(),
isEvent ? newTop : contentsY(),
visibleWidth(), visibleHeight() );
// some variables used to determine the viewport
int nearPageNumber = -1;
double viewportCenterX = (viewportRect.left() + viewportRect.right()) / 2.0,
viewportCenterY = (viewportRect.top() + viewportRect.bottom()) / 2.0,
focusedX = 0.5,
focusedY = 0.0,
minDistance = -1.0;
// iterate over all items
d->visibleItems.clear();
TQValueList< PixmapRequest * > requestedPixmaps;
TQValueVector< PageViewItem * >::iterator iIt = d->items.begin(), iEnd = d->items.end();
for ( ; iIt != iEnd; ++iIt )
{
PageViewItem * i = *iIt;
// if the item doesn't intersect the viewport, skip it
if ( !viewportRect.intersects( i->geometry() ) )
continue;
// add the item to the 'visible list'
d->visibleItems.push_back( i );
// if the item has not the right pixmap, add a request for it
if ( !i->page()->hasPixmap( PAGEVIEW_ID, i->width(), i->height() ) )
{
PixmapRequest * p = new PixmapRequest(
PAGEVIEW_ID, i->pageNumber(), i->width(), i->height(), PAGEVIEW_PRIO, true, d->rotation );
requestedPixmaps.push_back( p );
}
// look for the item closest to viewport center and the relative
// position between the item and the viewport center
if ( isEvent )
{
const TQRect & geometry = i->geometry();
// compute distance between item center and viewport center
double distance = hypot( (geometry.left() + geometry.right()) / 2 - viewportCenterX,
(geometry.top() + geometry.bottom()) / 2 - viewportCenterY );
if ( distance >= minDistance && nearPageNumber != -1 )
continue;
nearPageNumber = i->pageNumber();
minDistance = distance;
if ( geometry.height() > 0 && geometry.width() > 0 )
{
focusedX = ( viewportCenterX - (double)geometry.left() ) / (double)geometry.width();
focusedY = ( viewportCenterY - (double)geometry.top() ) / (double)geometry.height();
}
}
}
// if preloading is enabled, add the pages before and after in preloading
if ( !d->visibleItems.isEmpty() &&
KpdfSettings::memoryLevel() != KpdfSettings::EnumMemoryLevel::Low &&
KpdfSettings::enableThreading() )
{
// as the requests are done in the order as they appear in the list,
// request first the next page and then the previous
// add the page after the 'visible series' in preload
int tailRequest = d->visibleItems.last()->pageNumber() + 1;
if ( tailRequest < (int)d->items.count() )
{
PageViewItem * i = d->items[ tailRequest ];
// request the pixmap if not already present
if ( !i->page()->hasPixmap( PAGEVIEW_ID, i->width(), i->height() ) && i->width() > 0 )
requestedPixmaps.push_back( new PixmapRequest(
PAGEVIEW_ID, i->pageNumber(), i->width(), i->height(), PAGEVIEW_PRELOAD_PRIO, true, d->rotation ) );
}
// add the page before the 'visible series' in preload
int headRequest = d->visibleItems.first()->pageNumber() - 1;
if ( headRequest >= 0 )
{
PageViewItem * i = d->items[ headRequest ];
// request the pixmap if not already present
if ( !i->page()->hasPixmap( PAGEVIEW_ID, i->width(), i->height() ) && i->width() > 0 )
requestedPixmaps.push_back( new PixmapRequest(
PAGEVIEW_ID, i->pageNumber(), i->width(), i->height(), PAGEVIEW_PRELOAD_PRIO, true, d->rotation ) );
}
}
// send requests to the document
if ( !requestedPixmaps.isEmpty() )
d->document->requestPixmaps( requestedPixmaps );
// if this functions was invoked by viewport events, send update to document
if ( isEvent && nearPageNumber != -1 )
{
// determine the document viewport
DocumentViewport newViewport( nearPageNumber );
newViewport.rePos.enabled = true;
newViewport.rePos.normalizedX = focusedX;
newViewport.rePos.normalizedY = focusedY;
// set the viewport to other observers
d->document->setViewport( newViewport , PAGEVIEW_ID);
}
}
void PageView::slotMoveViewport()
{
// converge to viewportMoveDest in 1 second
int diffTime = d->viewportMoveTime.elapsed();
if ( diffTime >= 667 || !d->viewportMoveActive )
{
center( d->viewportMoveDest.x(), d->viewportMoveDest.y() );
d->viewportMoveTimer->stop();
d->viewportMoveActive = false;
slotRequestVisiblePixmaps();
verticalScrollBar()->setEnabled( true );
horizontalScrollBar()->setEnabled( true );
return;
}
// move the viewport smoothly (kmplot: p(x)=x+x*(1-x)*(1-x))
float convergeSpeed = (float)diffTime / 667.0,
x = ((float)visibleWidth() / 2.0) + contentsX(),
y = ((float)visibleHeight() / 2.0) + contentsY(),
diffX = (float)d->viewportMoveDest.x() - x,
diffY = (float)d->viewportMoveDest.y() - y;
convergeSpeed *= convergeSpeed * (1.4 - convergeSpeed);
center( (int)(x + diffX * convergeSpeed),
(int)(y + diffY * convergeSpeed ) );
}
void PageView::slotAutoScoll()
{
// the first time create the timer
if ( !d->autoScrollTimer )
{
d->autoScrollTimer = new TQTimer( this );
connect( d->autoScrollTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( slotAutoScoll() ) );
}
// if scrollIncrement is zero, stop the timer
if ( !d->scrollIncrement )
{
d->autoScrollTimer->stop();
return;
}
// compute delay between timer ticks and scroll amount per tick
int index = abs( d->scrollIncrement ) - 1; // 0..9
const int scrollDelay[10] = { 200, 100, 50, 30, 20, 30, 25, 20, 30, 20 };
const int scrollOffset[10] = { 1, 1, 1, 1, 1, 2, 2, 2, 4, 4 };
d->autoScrollTimer->changeInterval( scrollDelay[ index ] );
scrollBy( 0, d->scrollIncrement > 0 ? scrollOffset[ index ] : -scrollOffset[ index ] );
}
void PageView::slotDragScroll()
{
scrollBy(d->dragScrollVector.x(), d->dragScrollVector.y());
TQPoint p = viewportToContents( mapFromGlobal( TQCursor::pos() ) );
selectionEndPoint( p.x(), p.y() );
}
void PageView::findAheadStop()
{
d->typeAheadActive = false;
d->typeAheadString = "";
d->messageWindow->display( i18n("Find stopped."), PageViewMessage::Find, 1000 );
// it is needed to grab the keyboard becase people may have Space assigned to a
// accel and without grabbing the keyboard you can not vim-search for space
// because it activates the accel
releaseKeyboard();
}
void PageView::slotShowWelcome()
{
// show initial welcome text
d->messageWindow->display( i18n( "Welcome" ), PageViewMessage::Info, 2000 );
}
void PageView::slotZoom()
{
setFocus();
updateZoom( ZoomFixed );
}
void PageView::slotZoomIn()
{
updateZoom( ZoomIn );
}
void PageView::slotZoomOut()
{
updateZoom( ZoomOut );
}
void PageView::slotFitToWidthToggled( bool on )
{
if ( on ) updateZoom( ZoomFitWidth );
}
void PageView::slotFitToPageToggled( bool on )
{
if ( on ) updateZoom( ZoomFitPage );
}
void PageView::slotFitToTextToggled( bool on )
{
if ( on ) updateZoom( ZoomFitText );
}
void PageView::slotRotateRight()
{
d->rotation = ( d->rotation + 90 ) % 360;
TQValueVector< PageViewItem * >::iterator iIt = d->items.begin(), iEnd = d->items.end();
for ( ; iIt != iEnd; ++iIt )
{
int r = const_cast<KPDFPage*>((*iIt)->page())->rotation();
r = ( r + 90 ) % 360;
const_cast<KPDFPage*>((*iIt)->page())->rotate90degrees();
}
// be sure to block updates to document's viewport
bool prevState = d->blockViewport;
d->blockViewport = true;
slotRelayoutPages();
d->blockViewport = prevState;
// request pixmaps
slotRequestVisiblePixmaps();
}
void PageView::slotRotateLeft()
{
d->rotation -= 90;
if ( d->rotation < 0 ) d->rotation += 360;
TQValueVector< PageViewItem * >::iterator iIt = d->items.begin(), iEnd = d->items.end();
for ( ; iIt != iEnd; ++iIt )
{
int r = const_cast<KPDFPage*>((*iIt)->page())->rotation();
r -= 90;
if ( r < 0 ) r += 360;
const_cast<KPDFPage*>((*iIt)->page())->rotate90degrees();
}
// be sure to block updates to document's viewport
bool prevState = d->blockViewport;
d->blockViewport = true;
slotRelayoutPages();
d->blockViewport = prevState;
// request pixmaps
slotRequestVisiblePixmaps();
}
void PageView::slotTwoPagesToggled( bool on )
{
uint newColumns = on ? 2 : 1;
if ( KpdfSettings::viewColumns() != newColumns )
{
KpdfSettings::setViewColumns( newColumns );
KpdfSettings::writeConfig();
if ( d->document->pages() > 0 )
slotRelayoutPages();
}
}
void PageView::slotContinuousToggled( bool on )
{
if ( KpdfSettings::viewContinuous() != on )
{
KpdfSettings::setViewContinuous( on );
KpdfSettings::writeConfig();
if ( d->document->pages() > 0 )
slotRelayoutPages();
}
}
void PageView::slotSetMouseNormal()
{
d->mouseMode = MouseNormal;
d->messageWindow->hide();
}
void PageView::slotSetMouseZoom()
{
d->mouseMode = MouseZoom;
d->messageWindow->display( i18n( "Select zooming area. Right-click to zoom out." ), PageViewMessage::Info, -1 );
}
void PageView::slotSetMouseSelect()
{
d->mouseMode = MouseSelect;
d->messageWindow->display( i18n( "Draw a rectangle around the text/graphics to copy." ), PageViewMessage::Info, -1 );
}
void PageView::slotSetMouseDraw()
{
d->mouseMode = MouseEdit;
d->aMouseEdit->setChecked( true );
d->messageWindow->hide();
}
void PageView::slotScrollUp()
{
if ( d->scrollIncrement < -9 )
return;
d->scrollIncrement--;
slotAutoScoll();
setFocus();
}
void PageView::slotScrollDown()
{
if ( d->scrollIncrement > 9 )
return;
d->scrollIncrement++;
slotAutoScoll();
setFocus();
}
//END private SLOTS
#include "pageview.moc"