1 /***************************************************************************
2 * Copyright (C) 2004-2005 by Enrico Ros <eros.kde@email.it> *
3 * Copyright (C) 2004-2006 by Albert Astals Cid <tsdgeos@terra.es> *
5 * With portions of code from kpdf/kpdf_pagewidget.cc by: *
6 * Copyright (C) 2002 by Wilco Greven <greven@kde.org> *
7 * Copyright (C) 2003 by Christophe Devriese *
8 * <Christophe.Devriese@student.kuleuven.ac.be> *
9 * Copyright (C) 2003 by Laurent Montel <montel@kde.org> *
10 * Copyright (C) 2003 by Dirk Mueller <mueller@kde.org> *
11 * Copyright (C) 2004 by James Ots <kde@jamesots.com> *
13 * This program is free software; you can redistribute it and/or modify *
14 * it under the terms of the GNU General Public License as published by *
15 * the Free Software Foundation; either version 2 of the License, or *
16 * (at your option) any later version. *
17 ***************************************************************************/
28 #include <qscrollbar.h>
30 #include <qapplication.h>
31 #include <qclipboard.h>
34 #include <kstandardaction.h>
35 #include <kactioncollection.h>
38 #include <kfiledialog.h>
40 #include <kglobalsettings.h>
41 #include <kselectaction.h>
42 #include <ktoggleaction.h>
44 #include <kmessagebox.h>
52 #include "formwidgets.h"
53 #include "pageviewutils.h"
54 #include "pagepainter.h"
55 #include "core/annotations.h"
56 #include "annotwindow.h"
58 #include "annotationpopup.h"
59 #include "pageviewannotator.h"
60 #include "toolaction.h"
62 #include "videowidget.h"
63 #include "core/action.h"
64 #include "core/document.h"
65 #include "core/form.h"
66 #include "core/page.h"
67 #include "core/misc.h"
68 #include "core/generator.h"
69 #include "core/movie.h"
72 static int pageflags
= PagePainter::Accessibility
| PagePainter::EnhanceLinks
|
73 PagePainter::EnhanceImages
| PagePainter::Highlights
|
74 PagePainter::TextSelection
| PagePainter::Annotations
;
76 // structure used internally by PageView for data storage
80 PageViewPrivate( PageView
*qq
);
82 FormWidgetsController
* formWidgetsController();
84 QString
selectedText() const;
86 // the document, pageviewItems and the 'visible cache'
88 Okular::Document
* document
;
89 QVector
< PageViewItem
* > items
;
90 QLinkedList
< PageViewItem
* > visibleItems
;
92 // view layout (columns and continuous in Settings), zoom and mouse
93 PageView::ZoomMode zoomMode
;
95 PageView::MouseMode mouseMode
;
98 QPoint mouseSelectPos
;
102 QRect mouseSelectionRect
;
103 QColor mouseSelectionColor
;
104 bool mouseTextSelecting
;
105 QSet
< int > pagesWithTextSelection
;
107 Okular::Annotation
* mouseAnn
;
111 bool viewportMoveActive
;
112 QTime viewportMoveTime
;
113 QPoint viewportMoveDest
;
114 QTimer
* viewportMoveTimer
;
117 QTimer
* autoScrollTimer
;
119 PageViewAnnotator
* annotator
;
120 //text annotation dialogs list
121 QHash
< Okular::Annotation
*, AnnotWindow
* > m_annowindows
;
123 QTimer
* delayResizeTimer
;
125 bool blockViewport
; // prevents changes to viewport
126 bool blockPixmapsRequest
; // prevent pixmap requests
127 PageViewMessage
* messageWindow
; // in pageviewutils.h
129 FormWidgetsController
*formsWidgetController
;
131 QTimer
* refreshTimer
;
135 QPoint dragScrollVector
;
136 QTimer dragScrollTimer
;
139 KAction
* aRotateClockwise
;
140 KAction
* aRotateCounterClockwise
;
141 KAction
* aRotateOriginal
;
142 KSelectAction
* aPageSizes
;
143 KToggleAction
* aTrimMargins
;
144 QAction
* aMouseNormal
;
145 QAction
* aMouseSelect
;
146 QAction
* aMouseTextSelect
;
147 KToggleAction
* aToggleAnnotator
;
148 KSelectAction
* aZoom
;
151 KToggleAction
* aZoomFitWidth
;
152 KToggleAction
* aZoomFitPage
;
153 KToggleAction
* aZoomFitText
;
154 KSelectAction
* aViewMode
;
155 KToggleAction
* aViewContinuous
;
156 QAction
* aPrevAction
;
157 KAction
* aToggleForms
;
159 KAction
* aSpeakPage
;
160 KAction
* aSpeakStop
;
161 KActionCollection
* actionCollection
;
163 int setting_viewMode
;
164 int setting_viewCols
;
165 bool setting_centerFirst
;
168 PageViewPrivate::PageViewPrivate( PageView
*qq
)
173 FormWidgetsController
* PageViewPrivate::formWidgetsController()
175 if ( !formsWidgetController
)
177 formsWidgetController
= new FormWidgetsController();
178 QObject::connect( formsWidgetController
, SIGNAL( changed( FormWidgetIface
* ) ),
179 q
, SLOT( slotFormWidgetChanged( FormWidgetIface
* ) ) );
180 QObject::connect( formsWidgetController
, SIGNAL( action( Okular::Action
* ) ),
181 q
, SLOT( slotAction( Okular::Action
* ) ) );
184 return formsWidgetController
;
187 OkularTTS
* PageViewPrivate::tts()
191 m_tts
= new OkularTTS( q
);
194 QObject::connect( m_tts
, SIGNAL( hasSpeechs( bool ) ),
195 aSpeakStop
, SLOT( setEnabled( bool ) ) );
196 QObject::connect( m_tts
, SIGNAL( errorMessage( const QString
& ) ),
197 q
, SLOT( errorMessage( const QString
& ) ) );
205 class PageViewWidget
: public QWidget
208 PageViewWidget(PageView
*pv
) : QWidget(pv
), m_pageView(pv
) {}
211 bool event( QEvent
*e
)
213 if ( e
->type() == QEvent::ToolTip
&& m_pageView
->d
->mouseMode
== PageView::MouseNormal
)
215 QHelpEvent
* he
= (QHelpEvent
*)e
;
216 PageViewItem
* pageItem
= m_pageView
->pickItemOnPoint( he
->x(), he
->y() );
217 const Okular::ObjectRect
* rect
= 0;
218 const Okular::Action
* link
= 0;
219 const Okular::Annotation
* ann
= 0;
222 double nX
= pageItem
->absToPageX( he
->x() );
223 double nY
= pageItem
->absToPageY( he
->y() );
224 rect
= pageItem
->page()->objectRect( Okular::ObjectRect::OAnnotation
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
226 ann
= static_cast< const Okular::AnnotationObjectRect
* >( rect
)->annotation();
229 rect
= pageItem
->page()->objectRect( Okular::ObjectRect::Action
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
231 link
= static_cast< const Okular::Action
* >( rect
->object() );
237 QRect r
= rect
->boundingRect( pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
238 r
.translate( pageItem
->uncroppedGeometry().topLeft() );
239 QString tip
= GuiUtils::prettyToolTip( ann
);
240 QToolTip::showText( he
->globalPos(), tip
, this, r
);
244 QRect r
= rect
->boundingRect( pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
245 r
.translate( pageItem
->uncroppedGeometry().topLeft() );
246 QString tip
= link
->actionTip();
247 if ( !tip
.isEmpty() )
248 QToolTip::showText( he
->globalPos(), tip
, this, r
);
254 // do not stop the event
255 return QWidget::event( e
);
259 void paintEvent( QPaintEvent
*e
)
261 m_pageView
->contentsPaintEvent(e
);
264 void mouseMoveEvent( QMouseEvent
*e
)
266 m_pageView
->contentsMouseMoveEvent(e
);
269 void mousePressEvent( QMouseEvent
*e
)
271 m_pageView
->contentsMousePressEvent(e
);
274 void mouseReleaseEvent( QMouseEvent
*e
)
276 m_pageView
->contentsMouseReleaseEvent(e
);
281 PageView
*m_pageView
;
284 /* PageView. What's in this file? -> quick overview.
285 * Code weight (in rows) and meaning:
286 * 160 - constructor and creating actions plus their connected slots (empty stuff)
287 * 70 - DocumentObserver inherited methodes (important)
288 * 550 - events: mouse, keyboard, drag/drop
289 * 170 - slotRelayoutPages: set contents of the scrollview on continuous/single modes
290 * 100 - zoom: zooming pages in different ways, keeping update the toolbar actions, etc..
291 * other misc functions: only slotRequestVisiblePixmaps and pickItemOnPoint noticeable,
292 * and many insignificant stuff like this comment :-)
294 PageView::PageView( QWidget
*parent
, Okular::Document
*document
)
295 : QScrollArea( parent
)
296 , Okular::View( QString::fromLatin1( "PageView" ) )
298 // create and initialize private storage structure
299 d
= new PageViewPrivate( this );
300 d
->document
= document
;
301 d
->aRotateClockwise
= 0;
302 d
->aRotateCounterClockwise
= 0;
303 d
->aRotateOriginal
= 0;
305 d
->zoomMode
= PageView::ZoomFitWidth
;
307 d
->mouseMode
= MouseNormal
;
308 d
->mouseMidZooming
= false;
309 d
->mouseSelecting
= false;
310 d
->mouseTextSelecting
= false;
311 d
->mouseOnRect
= false;
313 d
->viewportMoveActive
= false;
314 d
->viewportMoveTimer
= 0;
315 d
->scrollIncrement
= 0;
316 d
->autoScrollTimer
= 0;
318 d
->delayResizeTimer
= 0;
319 d
->dirtyLayout
= false;
320 d
->blockViewport
= false;
321 d
->blockPixmapsRequest
= false;
322 d
->messageWindow
= new PageViewMessage(this);
323 d
->m_formsVisible
= false;
324 d
->formsWidgetController
= 0;
328 d
->aRotateClockwise
= 0;
329 d
->aRotateCounterClockwise
= 0;
330 d
->aRotateOriginal
= 0;
335 d
->aMouseTextSelect
= 0;
336 d
->aToggleAnnotator
= 0;
337 d
->aZoomFitWidth
= 0;
341 d
->aViewContinuous
= 0;
347 d
->actionCollection
= 0;
349 d
->setting_viewMode
= Okular::Settings::viewMode();
350 d
->setting_viewCols
= Okular::Settings::viewColumns();
351 d
->setting_centerFirst
= Okular::Settings::centerFirstPageInRow();
353 setFrameStyle(QFrame::NoFrame
);
355 setAttribute( Qt::WA_StaticContents
);
357 setObjectName( QLatin1String( "okular::pageView" ) );
359 // widget setup: setup focus, accept drops and track mouse
360 setWidget(new PageViewWidget(this));
361 viewport()->setFocusProxy( this );
362 viewport()->setFocusPolicy( Qt::StrongFocus
);
363 widget()->setAttribute( Qt::WA_OpaquePaintEvent
);
364 widget()->setAttribute( Qt::WA_NoSystemBackground
);
365 setAcceptDrops( true );
366 widget()->setMouseTracking( true );
367 setWidgetResizable(true);
369 // conntect the padding of the viewport to pixmaps requests
370 connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotRequestVisiblePixmaps(int)));
371 connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotRequestVisiblePixmaps(int)));
372 connect( &d
->dragScrollTimer
, SIGNAL(timeout()), this, SLOT(slotDragScroll()) );
374 // set a corner button to resize the view to the page size
375 // QPushButton * resizeButton = new QPushButton( viewport() );
376 // resizeButton->setPixmap( SmallIcon("crop") );
377 // setCornerWidget( resizeButton );
378 // resizeButton->setEnabled( false );
380 setAttribute( Qt::WA_InputMethodEnabled
, true );
382 // schedule the welcome message
383 QMetaObject::invokeMethod(this, "slotShowWelcome", Qt::QueuedConnection
);
386 PageView::~PageView()
389 d
->m_tts
->stopAllSpeechs();
391 // delete the local storage structure
392 qDeleteAll(d
->m_annowindows
);
393 // delete all widgets
394 QVector
< PageViewItem
* >::const_iterator dIt
= d
->items
.begin(), dEnd
= d
->items
.end();
395 for ( ; dIt
!= dEnd
; ++dIt
)
397 delete d
->formsWidgetController
;
398 d
->document
->removeObserver( this );
402 void PageView::setupBaseActions( KActionCollection
* ac
)
404 d
->actionCollection
= ac
;
406 // Zoom actions ( higher scales takes lots of memory! )
407 d
->aZoom
= new KSelectAction(KIcon( "zoom-original" ), i18n("Zoom"), this);
408 ac
->addAction("zoom_to", d
->aZoom
);
409 d
->aZoom
->setEditable( true );
410 d
->aZoom
->setMaxComboViewCount( 13 );
411 connect( d
->aZoom
, SIGNAL( triggered(QAction
*) ), this, SLOT( slotZoom() ) );
414 d
->aZoomIn
= KStandardAction::zoomIn( this, SLOT( slotZoomIn() ), ac
);
416 d
->aZoomOut
= KStandardAction::zoomOut( this, SLOT( slotZoomOut() ), ac
);
419 void PageView::setupActions( KActionCollection
* ac
)
421 d
->actionCollection
= ac
;
423 // orientation menu actions
424 d
->aRotateClockwise
= new KAction( KIcon( "object-rotate-right" ), i18n( "Rotate Right" ), this );
425 d
->aRotateClockwise
->setIconText( i18nc( "Rotate right", "Right" ) );
426 ac
->addAction( "view_orientation_rotate_cw", d
->aRotateClockwise
);
427 d
->aRotateClockwise
->setEnabled( false );
428 connect( d
->aRotateClockwise
, SIGNAL( triggered() ), this, SLOT( slotRotateClockwise() ) );
429 d
->aRotateCounterClockwise
= new KAction( KIcon( "object-rotate-left" ), i18n( "Rotate Left" ), this );
430 d
->aRotateCounterClockwise
->setIconText( i18nc( "Rotate left", "Left" ) );
431 ac
->addAction( "view_orientation_rotate_ccw", d
->aRotateCounterClockwise
);
432 d
->aRotateCounterClockwise
->setEnabled( false );
433 connect( d
->aRotateCounterClockwise
, SIGNAL( triggered() ), this, SLOT( slotRotateCounterClockwise() ) );
434 d
->aRotateOriginal
= new KAction( i18n( "Original Orientation" ), this );
435 ac
->addAction( "view_orientation_original", d
->aRotateOriginal
);
436 d
->aRotateOriginal
->setEnabled( false );
437 connect( d
->aRotateOriginal
, SIGNAL( triggered() ), this, SLOT( slotRotateOriginal() ) );
439 d
->aPageSizes
= new KSelectAction(i18n("&Page Size"), this);
440 ac
->addAction("view_pagesizes", d
->aPageSizes
);
441 d
->aPageSizes
->setEnabled( false );
443 connect( d
->aPageSizes
, SIGNAL( triggered( int ) ),
444 this, SLOT( slotPageSizes( int ) ) );
446 d
->aTrimMargins
= new KToggleAction( i18n( "&Trim Margins" ), this );
447 ac
->addAction("view_trim_margins", d
->aTrimMargins
);
448 connect( d
->aTrimMargins
, SIGNAL( toggled( bool ) ), SLOT( slotTrimMarginsToggled( bool ) ) );
449 d
->aTrimMargins
->setChecked( Okular::Settings::trimMargins() );
451 d
->aZoomFitWidth
= new KToggleAction(KIcon( "zoom-fit-width" ), i18n("Fit &Width"), this);
452 ac
->addAction("view_fit_to_width", d
->aZoomFitWidth
);
453 connect( d
->aZoomFitWidth
, SIGNAL( toggled( bool ) ), SLOT( slotFitToWidthToggled( bool ) ) );
455 d
->aZoomFitPage
= new KToggleAction(KIcon( "zoom-fit-best" ), i18n("Fit &Page"), this);
456 ac
->addAction("view_fit_to_page", d
->aZoomFitPage
);
457 connect( d
->aZoomFitPage
, SIGNAL( toggled( bool ) ), SLOT( slotFitToPageToggled( bool ) ) );
460 d->aZoomFitText = new KToggleAction(KIcon( "zoom-fit-best" ), i18n("Fit &Text"), this);
461 ac->addAction("zoom_fit_text", d->aZoomFitText );
462 connect( d->aZoomFitText, SIGNAL( toggled( bool ) ), SLOT( slotFitToTextToggled( bool ) ) );
465 // View-Layout actions
466 QStringList viewModes
;
467 viewModes
.append( i18n( "Single Page" ) );
468 viewModes
.append( i18n( "Facing Pages" ) );
469 viewModes
.append( i18n( "Overview" ) );
471 d
->aViewMode
= new KSelectAction(KIcon( "view-split-left-right" ), i18n("&View Mode"), this);
472 ac
->addAction("view_render_mode", d
->aViewMode
);
473 connect( d
->aViewMode
, SIGNAL( triggered( int ) ), SLOT( slotViewMode( int ) ) );
474 d
->aViewMode
->setItems( viewModes
);
475 d
->aViewMode
->setCurrentItem( Okular::Settings::viewMode() );
477 d
->aViewContinuous
= new KToggleAction(KIcon( "view-list-text" ), i18n("&Continuous"), this);
478 ac
->addAction("view_continuous", d
->aViewContinuous
);
479 connect( d
->aViewContinuous
, SIGNAL( toggled( bool ) ), SLOT( slotContinuousToggled( bool ) ) );
480 d
->aViewContinuous
->setChecked( Okular::Settings::viewContinuous() );
482 // Mouse-Mode actions
483 QActionGroup
* actGroup
= new QActionGroup( this );
484 actGroup
->setExclusive( true );
485 d
->aMouseNormal
= new KAction( KIcon( "input-mouse" ), i18n( "&Browse Tool" ), this );
486 ac
->addAction("mouse_drag", d
->aMouseNormal
);
487 connect( d
->aMouseNormal
, SIGNAL( triggered() ), this, SLOT( slotSetMouseNormal() ) );
488 d
->aMouseNormal
->setIconText( i18nc( "Browse Tool", "Browse" ) );
489 d
->aMouseNormal
->setCheckable( true );
490 d
->aMouseNormal
->setShortcut( Qt::CTRL
+ Qt::Key_1
);
491 d
->aMouseNormal
->setActionGroup( actGroup
);
492 d
->aMouseNormal
->setChecked( true );
494 KAction
* mz
= new KAction(KIcon( "zoom-original" ), i18n("&Zoom Tool"), this);
495 ac
->addAction("mouse_zoom", mz
);
496 connect( mz
, SIGNAL( triggered() ), this, SLOT( slotSetMouseZoom() ) );
497 mz
->setIconText( i18nc( "Zoom Tool", "Zoom" ) );
498 mz
->setCheckable( true );
499 mz
->setShortcut( Qt::CTRL
+ Qt::Key_2
);
500 mz
->setActionGroup( actGroup
);
502 d
->aMouseSelect
= new KAction(KIcon( "select-rectangular" ), i18n("&Selection Tool"), this);
503 ac
->addAction("mouse_select", d
->aMouseSelect
);
504 connect( d
->aMouseSelect
, SIGNAL( triggered() ), this, SLOT( slotSetMouseSelect() ) );
505 d
->aMouseSelect
->setIconText( i18nc( "Select Tool", "Selection" ) );
506 d
->aMouseSelect
->setCheckable( true );
507 d
->aMouseSelect
->setShortcut( Qt::CTRL
+ Qt::Key_3
);
508 d
->aMouseSelect
->setActionGroup( actGroup
);
510 d
->aMouseTextSelect
= new KAction(KIcon( "draw-text" ), i18n("&Text Selection Tool"), this);
511 ac
->addAction("mouse_textselect", d
->aMouseTextSelect
);
512 connect( d
->aMouseTextSelect
, SIGNAL( triggered() ), this, SLOT( slotSetMouseTextSelect() ) );
513 d
->aMouseTextSelect
->setIconText( i18nc( "Text Selection Tool", "Text Selection" ) );
514 d
->aMouseTextSelect
->setCheckable( true );
515 d
->aMouseTextSelect
->setShortcut( Qt::CTRL
+ Qt::Key_4
);
516 d
->aMouseTextSelect
->setActionGroup( actGroup
);
518 d
->aToggleAnnotator
= new KToggleAction(KIcon( "draw-freehand" ), i18n("&Review"), this);
519 ac
->addAction("mouse_toggle_annotate", d
->aToggleAnnotator
);
520 d
->aToggleAnnotator
->setCheckable( true );
521 connect( d
->aToggleAnnotator
, SIGNAL( toggled( bool ) ), SLOT( slotToggleAnnotator( bool ) ) );
522 d
->aToggleAnnotator
->setShortcut( Qt::Key_F6
);
524 ToolAction
*ta
= new ToolAction( this );
525 ac
->addAction( "mouse_selecttools", ta
);
526 ta
->addAction( d
->aMouseSelect
);
527 ta
->addAction( d
->aMouseTextSelect
);
530 d
->aSpeakDoc
= new KAction( KIcon( "text-speak" ), i18n( "Speak Whole Document" ), this );
531 ac
->addAction( "speak_document", d
->aSpeakDoc
);
532 d
->aSpeakDoc
->setEnabled( false );
533 connect( d
->aSpeakDoc
, SIGNAL( triggered() ), SLOT( slotSpeakDocument() ) );
535 d
->aSpeakPage
= new KAction( KIcon( "text-speak" ), i18n( "Speak Current Page" ), this );
536 ac
->addAction( "speak_current_page", d
->aSpeakPage
);
537 d
->aSpeakPage
->setEnabled( false );
538 connect( d
->aSpeakPage
, SIGNAL( triggered() ), SLOT( slotSpeakCurrentPage() ) );
540 d
->aSpeakStop
= new KAction( KIcon( "media-playback-stop" ), i18n( "Stop Speaking" ), this );
541 ac
->addAction( "speak_stop_all", d
->aSpeakStop
);
542 d
->aSpeakStop
->setEnabled( false );
543 connect( d
->aSpeakStop
, SIGNAL( triggered() ), SLOT( slotStopSpeaks() ) );
546 KAction
* su
= new KAction(i18n("Scroll Up"), this);
547 ac
->addAction("view_scroll_up", su
);
548 connect( su
, SIGNAL( triggered() ), this, SLOT( slotScrollUp() ) );
549 su
->setShortcut( QKeySequence(Qt::SHIFT
+ Qt::Key_Up
) );
552 KAction
* sd
= new KAction(i18n("Scroll Down"), this);
553 ac
->addAction("view_scroll_down", sd
);
554 connect( sd
, SIGNAL( triggered() ), this, SLOT( slotScrollDown() ) );
555 sd
->setShortcut( QKeySequence(Qt::SHIFT
+ Qt::Key_Down
) );
558 d
->aToggleForms
= new KAction( this );
559 ac
->addAction( "view_toggle_forms", d
->aToggleForms
);
560 connect( d
->aToggleForms
, SIGNAL( triggered() ), this, SLOT( slotToggleForms() ) );
561 d
->aToggleForms
->setEnabled( false );
562 toggleFormWidgets( false );
565 bool PageView::canFitPageWidth() const
567 return Okular::Settings::viewMode() != 0 || d
->zoomMode
!= ZoomFitWidth
;
570 void PageView::fitPageWidth( int page
)
572 // zoom: Fit Width, columns: 1. setActions + relayout + setPage + update
573 d
->zoomMode
= ZoomFitWidth
;
574 Okular::Settings::setViewMode( 0 );
575 d
->aZoomFitWidth
->setChecked( true );
576 d
->aZoomFitPage
->setChecked( false );
577 // d->aZoomFitText->setChecked( false );
578 d
->aViewMode
->setCurrentItem( 0 );
579 viewport()->setUpdatesEnabled( false );
581 viewport()->setUpdatesEnabled( true );
582 d
->document
->setViewportPage( page
);
587 void PageView::setAnnotationWindow( Okular::Annotation
* annotation
)
592 // find the annot window
593 AnnotWindow
* existWindow
= 0;
594 QHash
< Okular::Annotation
*, AnnotWindow
* >::ConstIterator it
= d
->m_annowindows
.find( annotation
);
595 if ( it
!= d
->m_annowindows
.end() )
600 if ( existWindow
== 0 )
602 existWindow
= new AnnotWindow( this, annotation
);
604 d
->m_annowindows
.insert( annotation
, existWindow
);
610 void PageView::removeAnnotationWindow( Okular::Annotation
*annotation
)
612 QHash
< Okular::Annotation
*, AnnotWindow
* >::Iterator it
= d
->m_annowindows
.find( annotation
);
613 if ( it
!= d
->m_annowindows
.end() )
616 d
->m_annowindows
.erase( it
);
620 void PageView::displayMessage( const QString
& message
,PageViewMessage::Icon icon
,int duration
)
622 if ( !Okular::Settings::showOSD() )
624 if (icon
== PageViewMessage::Error
)
625 KMessageBox::error( this, message
);
630 // hide messageWindow if string is empty
631 if ( message
.isEmpty() )
632 return d
->messageWindow
->hide();
634 // display message (duration is length dependant)
636 duration
= 500 + 100 * message
.length();
637 d
->messageWindow
->display( message
, icon
, duration
);
640 void PageView::reparseConfig()
642 // set the scroll bars policies
643 Qt::ScrollBarPolicy scrollBarMode
= Okular::Settings::showScrollBars() ?
644 Qt::ScrollBarAsNeeded
: Qt::ScrollBarAlwaysOff
;
645 if ( horizontalScrollBarPolicy() != scrollBarMode
)
647 setHorizontalScrollBarPolicy( scrollBarMode
);
648 setVerticalScrollBarPolicy( scrollBarMode
);
651 const int viewMode
= Okular::Settings::viewMode();
652 if ( ( viewMode
== 2 && ( (int)Okular::Settings::viewColumns() != d
->setting_viewCols
) )
653 || ( viewMode
> 0 && ( Okular::Settings::centerFirstPageInRow() != d
->setting_centerFirst
) )
656 d
->setting_viewMode
= Okular::Settings::viewMode();
657 d
->setting_viewCols
= Okular::Settings::viewColumns();
658 d
->setting_centerFirst
= Okular::Settings::centerFirstPageInRow();
664 KAction
*PageView::toggleFormsAction() const
666 return d
->aToggleForms
;
669 QString
PageViewPrivate::selectedText() const
671 if ( pagesWithTextSelection
.isEmpty() )
675 QList
< int > selpages
= pagesWithTextSelection
.toList();
677 const Okular::Page
* pg
= 0;
678 if ( selpages
.count() == 1 )
680 pg
= document
->page( selpages
.first() );
681 text
.append( pg
->text( pg
->textSelection() ) );
685 pg
= document
->page( selpages
.first() );
686 text
.append( pg
->text( pg
->textSelection() ) );
687 int end
= selpages
.count() - 1;
688 for( int i
= 1; i
< end
; ++i
)
690 pg
= document
->page( selpages
.at( i
) );
691 text
.append( pg
->text() );
693 pg
= document
->page( selpages
.last() );
694 text
.append( pg
->text( pg
->textSelection() ) );
699 void PageView::copyTextSelection() const
701 const QString text
= d
->selectedText();
702 if ( !text
.isEmpty() )
704 QClipboard
*cb
= QApplication::clipboard();
705 cb
->setText( text
, QClipboard::Clipboard
);
709 void PageView::selectAll()
711 if ( d
->mouseMode
!= MouseTextSelect
)
714 QVector
< PageViewItem
* >::const_iterator it
= d
->items
.begin(), itEnd
= d
->items
.end();
715 for ( ; it
< itEnd
; ++it
)
717 Okular::RegularAreaRect
* area
= textSelectionForItem( *it
);
718 d
->pagesWithTextSelection
.insert( (*it
)->pageNumber() );
719 d
->document
->setPageTextSelection( (*it
)->pageNumber(), area
, palette().color( QPalette::Active
, QPalette::Highlight
) );
723 //BEGIN DocumentObserver inherited methods
724 void PageView::notifySetup( const QVector
< Okular::Page
* > & pageSet
, int setupFlags
)
726 bool documentChanged
= setupFlags
& Okular::DocumentObserver::DocumentChanged
;
727 // reuse current pages if nothing new
728 if ( ( pageSet
.count() == d
->items
.count() ) && !documentChanged
&& !( setupFlags
& Okular::DocumentObserver::NewLayoutForPages
) )
730 int count
= pageSet
.count();
731 for ( int i
= 0; (i
< count
) && !documentChanged
; i
++ )
732 if ( (int)pageSet
[i
]->number() != d
->items
[i
]->pageNumber() )
733 documentChanged
= true;
734 if ( !documentChanged
)
738 // delete all widgets (one for each page in pageSet)
739 QVector
< PageViewItem
* >::const_iterator dIt
= d
->items
.begin(), dEnd
= d
->items
.end();
740 for ( ; dIt
!= dEnd
; ++dIt
)
743 d
->visibleItems
.clear();
744 d
->pagesWithTextSelection
.clear();
745 toggleFormWidgets( false );
746 if ( d
->formsWidgetController
)
747 d
->formsWidgetController
->dropRadioButtons();
749 bool haspages
= !pageSet
.isEmpty();
750 bool hasformwidgets
= false;
751 // create children widgets
752 QVector
< Okular::Page
* >::const_iterator setIt
= pageSet
.begin(), setEnd
= pageSet
.end();
753 for ( ; setIt
!= setEnd
; ++setIt
)
755 PageViewItem
* item
= new PageViewItem( *setIt
);
756 d
->items
.push_back( item
);
757 #ifdef PAGEVIEW_DEBUG
758 kDebug().nospace() << "cropped geom for " << d
->items
.last()->pageNumber() << " is " << d
->items
.last()->croppedGeometry();
760 const QLinkedList
< Okular::FormField
* > pageFields
= (*setIt
)->formFields();
761 QLinkedList
< Okular::FormField
* >::const_iterator ffIt
= pageFields
.begin(), ffEnd
= pageFields
.end();
762 for ( ; ffIt
!= ffEnd
; ++ffIt
)
764 Okular::FormField
* ff
= *ffIt
;
765 FormWidgetIface
* w
= FormWidgetFactory::createWidget( ff
, widget() );
768 w
->setPageItem( item
);
769 w
->setFormWidgetsController( d
->formWidgetsController() );
770 w
->setVisibility( false );
771 w
->setCanBeFilled( d
->document
->isAllowed( Okular::AllowFillForms
) );
772 item
->formWidgets().insert( ff
->id(), w
);
773 hasformwidgets
= true;
776 const QLinkedList
< Okular::Annotation
* > annotations
= (*setIt
)->annotations();
777 QLinkedList
< Okular::Annotation
* >::const_iterator aIt
= annotations
.begin(), aEnd
= annotations
.end();
778 for ( ; aIt
!= aEnd
; ++aIt
)
780 Okular::Annotation
* a
= *aIt
;
781 if ( a
->subType() == Okular::Annotation::AMovie
)
783 Okular::MovieAnnotation
* movieAnn
= static_cast< Okular::MovieAnnotation
* >( a
);
784 VideoWidget
* vw
= new VideoWidget( movieAnn
, d
->document
, widget() );
785 item
->videoWidgets().insert( movieAnn
->movie(), vw
);
791 // invalidate layout so relayout/repaint will happen on next viewport change
793 // TODO for Enrico: Check if doing always the slotRelayoutPages() is not
794 // suboptimal in some cases, i'd say it is not but a recheck will not hurt
795 // Need slotRelayoutPages() here instead of d->dirtyLayout = true
796 // because opening a document from another document will not trigger a viewportchange
797 // so pages are never relayouted
798 QMetaObject::invokeMethod(this, "slotRelayoutPages", Qt::QueuedConnection
);
801 // update the mouse cursor when closing because we may have close through a link and
802 // want the cursor to come back to the normal cursor
803 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
804 setWidgetResizable(true);
807 // OSD to display pages
808 if ( documentChanged
&& pageSet
.count() > 0 && Okular::Settings::showOSD() )
809 d
->messageWindow
->display(
810 i18np(" Loaded a one-page document.",
811 " Loaded a %1-page document.",
813 PageViewMessage::Info
, 4000 );
816 { // may be null if dummy mode is on
817 bool pageSizes
= d
->document
->supportsPageSizes();
818 d
->aPageSizes
->setEnabled( pageSizes
);
819 // set the new page sizes:
820 // - if the generator supports them
821 // - if the document changed
822 if ( pageSizes
&& documentChanged
)
825 foreach ( const Okular::PageSize
&p
, d
->document
->pageSizes() )
826 items
.append( p
.name() );
827 d
->aPageSizes
->setItems( items
);
830 if ( d
->aRotateClockwise
)
831 d
->aRotateClockwise
->setEnabled( haspages
);
832 if ( d
->aRotateCounterClockwise
)
833 d
->aRotateCounterClockwise
->setEnabled( haspages
);
834 if ( d
->aRotateOriginal
)
835 d
->aRotateOriginal
->setEnabled( haspages
);
836 if ( d
->aToggleForms
)
837 { // may be null if dummy mode is on
838 d
->aToggleForms
->setEnabled( haspages
&& hasformwidgets
);
840 bool allowAnnotations
= d
->document
->isAllowed( Okular::AllowNotes
);
843 bool allowTools
= haspages
&& allowAnnotations
;
844 d
->annotator
->setToolsEnabled( allowTools
);
845 d
->annotator
->setTextToolsEnabled( allowTools
&& d
->document
->supportsSearching() );
847 if ( d
->aToggleAnnotator
)
849 if ( !allowAnnotations
&& d
->aToggleAnnotator
->isChecked() )
851 d
->aToggleAnnotator
->trigger();
853 d
->aToggleAnnotator
->setEnabled( allowAnnotations
);
857 const bool enablettsactions
= haspages
? Okular::Settings::useKTTSD() : false;
858 d
->aSpeakDoc
->setEnabled( enablettsactions
);
859 d
->aSpeakPage
->setEnabled( enablettsactions
);
863 void PageView::notifyViewportChanged( bool smoothMove
)
865 // if we are the one changing viewport, skip this nofity
866 if ( d
->blockViewport
)
869 // block setViewport outgoing calls
870 d
->blockViewport
= true;
872 // find PageViewItem matching the viewport description
873 const Okular::DocumentViewport
& vp
= d
->document
->viewport();
874 PageViewItem
* item
= 0;
875 QVector
< PageViewItem
* >::const_iterator iIt
= d
->items
.begin(), iEnd
= d
->items
.end();
876 for ( ; iIt
!= iEnd
; ++iIt
)
877 if ( (*iIt
)->pageNumber() == vp
.pageNumber
)
884 kWarning() << "viewport for page" << vp
.pageNumber
<< "has no matching item!";
885 d
->blockViewport
= false;
888 #ifdef PAGEVIEW_DEBUG
889 kDebug() << "document viewport changed";
891 // relayout in "Single Pages" mode or if a relayout is pending
892 d
->blockPixmapsRequest
= true;
893 if ( !Okular::Settings::viewContinuous() || d
->dirtyLayout
)
896 // restore viewport center or use default {x-center,v-top} alignment
897 const QRect
& r
= item
->croppedGeometry();
898 int newCenterX
= r
.left(),
899 newCenterY
= r
.top();
900 if ( vp
.rePos
.enabled
)
902 if ( vp
.rePos
.pos
== Okular::DocumentViewport::Center
)
904 newCenterX
+= (int)( vp
.rePos
.normalizedX
* (double)r
.width() );
905 newCenterY
+= (int)( vp
.rePos
.normalizedY
* (double)r
.height() );
910 newCenterX
+= (int)( vp
.rePos
.normalizedX
* (double)r
.width() + viewport()->width() / 2 );
911 newCenterY
+= (int)( vp
.rePos
.normalizedY
* (double)r
.height() + viewport()->height() / 2 );
916 newCenterX
+= r
.width() / 2;
917 newCenterY
+= viewport()->height() / 2 - 10;
920 // if smooth movement requested, setup parameters and start it
923 d
->viewportMoveActive
= true;
924 d
->viewportMoveTime
.start();
925 d
->viewportMoveDest
.setX( newCenterX
);
926 d
->viewportMoveDest
.setY( newCenterY
);
927 if ( !d
->viewportMoveTimer
)
929 d
->viewportMoveTimer
= new QTimer( this );
930 connect( d
->viewportMoveTimer
, SIGNAL( timeout() ),
931 this, SLOT( slotMoveViewport() ) );
933 d
->viewportMoveTimer
->start( 25 );
934 verticalScrollBar()->setEnabled( false );
935 horizontalScrollBar()->setEnabled( false );
938 center( newCenterX
, newCenterY
);
939 d
->blockPixmapsRequest
= false;
941 // request visible pixmaps in the current viewport and recompute it
942 slotRequestVisiblePixmaps();
944 // enable setViewport calls
945 d
->blockViewport
= false;
947 // update zoom text if in a ZoomFit/* zoom mode
948 if ( d
->zoomMode
!= ZoomFixed
)
951 // since the page has moved below cursor, update it
952 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
955 void PageView::notifyPageChanged( int pageNumber
, int changedFlags
)
957 // only handle pixmap / highlight changes notifies
958 if ( changedFlags
& DocumentObserver::Bookmark
)
961 if ( changedFlags
& DocumentObserver::Annotations
)
963 const QLinkedList
< Okular::Annotation
* > annots
= d
->document
->page( pageNumber
)->annotations();
964 const QLinkedList
< Okular::Annotation
* >::ConstIterator annItEnd
= annots
.end();
965 QHash
< Okular::Annotation
*, AnnotWindow
* >::Iterator it
= d
->m_annowindows
.begin();
966 for ( ; it
!= d
->m_annowindows
.end(); )
968 QLinkedList
< Okular::Annotation
* >::ConstIterator annIt
= qFind( annots
, it
.key() );
969 if ( annIt
!= annItEnd
)
977 it
= d
->m_annowindows
.erase( it
);
982 if ( changedFlags
& DocumentObserver::BoundingBox
)
984 #ifdef PAGEVIEW_DEBUG
985 kDebug() << "BoundingBox change on page" << pageNumber
;
988 slotRequestVisiblePixmaps(); // TODO: slotRelayoutPages() may have done this already!
989 // Repaint the whole widget since layout may have changed
994 // iterate over visible items: if page(pageNumber) is one of them, repaint it
995 QLinkedList
< PageViewItem
* >::const_iterator iIt
= d
->visibleItems
.begin(), iEnd
= d
->visibleItems
.end();
996 for ( ; iIt
!= iEnd
; ++iIt
)
997 if ( (*iIt
)->pageNumber() == pageNumber
&& (*iIt
)->isVisible() )
999 // update item's rectangle plus the little outline
1000 QRect expandedRect
= (*iIt
)->croppedGeometry();
1001 expandedRect
.adjust( -1, -1, 3, 3 );
1002 widget()->update( expandedRect
);
1004 // if we were "zoom-dragging" do not overwrite the "zoom-drag" cursor
1005 if ( cursor().shape() != Qt::SizeVerCursor
)
1007 // since the page has been regenerated below cursor, update it
1008 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
1014 void PageView::notifyContentsCleared( int changedFlags
)
1016 // if pixmaps were cleared, re-ask them
1017 if ( changedFlags
& DocumentObserver::Pixmap
)
1018 QMetaObject::invokeMethod(this, "slotRequestVisiblePixmaps", Qt::QueuedConnection
);
1021 void PageView::notifyZoom( int factor
)
1024 updateZoom( ZoomIn
);
1026 updateZoom( ZoomOut
);
1029 bool PageView::canUnloadPixmap( int pageNumber
) const
1031 if ( Okular::Settings::memoryLevel() != Okular::Settings::EnumMemoryLevel::Aggressive
)
1033 // if the item is visible, forbid unloading
1034 QLinkedList
< PageViewItem
* >::const_iterator vIt
= d
->visibleItems
.begin(), vEnd
= d
->visibleItems
.end();
1035 for ( ; vIt
!= vEnd
; ++vIt
)
1036 if ( (*vIt
)->pageNumber() == pageNumber
)
1041 // forbid unloading of the visible items, and of the previous and next
1042 QLinkedList
< PageViewItem
* >::const_iterator vIt
= d
->visibleItems
.begin(), vEnd
= d
->visibleItems
.end();
1043 for ( ; vIt
!= vEnd
; ++vIt
)
1044 if ( abs( (*vIt
)->pageNumber() - pageNumber
) <= 1 )
1047 // if hidden premit unloading
1050 //END DocumentObserver inherited methods
1052 //BEGIN View inherited methods
1053 bool PageView::supportsCapability( ViewCapability capability
) const
1055 switch ( capability
)
1064 Okular::View::CapabilityFlags
PageView::capabilityFlags( ViewCapability capability
) const
1066 switch ( capability
)
1070 return CapabilityRead
| CapabilityWrite
| CapabilitySerializable
;
1075 QVariant
PageView::capability( ViewCapability capability
) const
1077 switch ( capability
)
1080 return d
->zoomFactor
;
1087 void PageView::setCapability( ViewCapability capability
, const QVariant
&option
)
1089 switch ( capability
)
1094 double factor
= option
.toDouble( &ok
);
1095 if ( ok
&& factor
> 0.0 )
1097 d
->zoomFactor
= static_cast< float >( factor
);
1098 updateZoom( ZoomRefreshCurrent
);
1105 int mode
= option
.toInt( &ok
);
1108 if ( mode
>= 0 && mode
< 3 )
1109 updateZoom( (ZoomMode
)mode
);
1116 //END View inherited methods
1118 //BEGIN widget events
1119 void PageView::contentsPaintEvent(QPaintEvent
*pe
)
1121 // create the rect into contents from the clipped screen rect
1122 QRect viewportRect
= viewport()->rect();
1123 viewportRect
.translate( horizontalScrollBar()->value(), verticalScrollBar()->value() );
1124 QRect contentsRect
= pe
->rect().intersect( viewportRect
);
1125 if ( !contentsRect
.isValid() )
1128 #ifdef PAGEVIEW_DEBUG
1129 kDebug() << "paintevent" << contentsRect
;
1132 // create the screen painter. a pixel painted at contentsX,contentsY
1133 // appears to the top-left corner of the scrollview.
1134 QPainter
screenPainter( widget() );
1136 // selectionRect is the normalized mouse selection rect
1137 QRect selectionRect
= d
->mouseSelectionRect
;
1138 if ( !selectionRect
.isNull() )
1139 selectionRect
= selectionRect
.normalized();
1140 // selectionRectInternal without the border
1141 QRect selectionRectInternal
= selectionRect
;
1142 selectionRectInternal
.adjust( 1, 1, -1, -1 );
1143 // color for blending
1144 QColor selBlendColor
= (selectionRect
.width() > 8 || selectionRect
.height() > 8) ?
1145 d
->mouseSelectionColor
: Qt::red
;
1147 // subdivide region into rects
1148 const QVector
<QRect
> &allRects
= pe
->region().rects();
1149 uint numRects
= allRects
.count();
1151 // preprocess rects area to see if it worths or not using subdivision
1152 uint summedArea
= 0;
1153 for ( uint i
= 0; i
< numRects
; i
++ )
1155 const QRect
& r
= allRects
[i
];
1156 summedArea
+= r
.width() * r
.height();
1158 // very elementary check: SUMj(Region[j].area) is less than boundingRect.area
1159 bool useSubdivision
= summedArea
< (0.6 * contentsRect
.width() * contentsRect
.height());
1160 if ( !useSubdivision
)
1163 // iterate over the rects (only one loop if not using subdivision)
1164 for ( uint i
= 0; i
< numRects
; i
++ )
1166 if ( useSubdivision
)
1168 // set 'contentsRect' to a part of the sub-divided region
1169 contentsRect
= allRects
[i
].normalized().intersect( viewportRect
);
1170 if ( !contentsRect
.isValid() )
1173 #ifdef PAGEVIEW_DEBUG
1174 kDebug() << contentsRect
;
1177 // note: this check will take care of all things requiring alpha blending (not only selection)
1178 bool wantCompositing
= !selectionRect
.isNull() && contentsRect
.intersects( selectionRect
);
1180 if ( wantCompositing
&& Okular::Settings::enableCompositing() )
1182 // create pixmap and open a painter over it (contents{left,top} becomes pixmap {0,0})
1183 QPixmap
doubleBuffer( contentsRect
.size() );
1184 QPainter
pixmapPainter( &doubleBuffer
);
1185 pixmapPainter
.translate( -contentsRect
.left(), -contentsRect
.top() );
1187 // 1) Layer 0: paint items and clear bg on unpainted rects
1188 drawDocumentOnPainter( contentsRect
, &pixmapPainter
);
1189 // 2) Layer 1a: paint (blend) transparent selection
1190 if ( !selectionRect
.isNull() && selectionRect
.intersects( contentsRect
) &&
1191 !selectionRectInternal
.contains( contentsRect
) )
1193 QRect blendRect
= selectionRectInternal
.intersect( contentsRect
);
1194 // skip rectangles covered by the selection's border
1195 if ( blendRect
.isValid() )
1197 // grab current pixmap into a new one to colorize contents
1198 QPixmap
blendedPixmap( blendRect
.width(), blendRect
.height() );
1199 QPainter
p( &blendedPixmap
);
1200 p
.drawPixmap( 0, 0, doubleBuffer
,
1201 blendRect
.left() - contentsRect
.left(), blendRect
.top() - contentsRect
.top(),
1202 blendRect
.width(), blendRect
.height() );
1204 QColor blCol
= selBlendColor
.dark( 140 );
1205 blCol
.setAlphaF( 0.2 );
1206 p
.fillRect( blendedPixmap
.rect(), blCol
);
1208 // copy the blended pixmap back to its place
1209 pixmapPainter
.drawPixmap( blendRect
.left(), blendRect
.top(), blendedPixmap
);
1211 // draw border (red if the selection is too small)
1212 pixmapPainter
.setPen( selBlendColor
);
1213 pixmapPainter
.drawRect( selectionRect
.adjusted( 0, 0, -1, -1 ) );
1215 // 3) Layer 1: give annotator painting control
1216 if ( d
->annotator
&& d
->annotator
->routePaints( contentsRect
) )
1217 d
->annotator
->routePaint( &pixmapPainter
, contentsRect
);
1218 // 4) Layer 2: overlays
1219 if ( Okular::Settings::debugDrawBoundaries() )
1221 pixmapPainter
.setPen( Qt::blue
);
1222 pixmapPainter
.drawRect( contentsRect
);
1225 // finish painting and draw contents
1226 pixmapPainter
.end();
1227 screenPainter
.drawPixmap( contentsRect
.left(), contentsRect
.top(), doubleBuffer
);
1231 // 1) Layer 0: paint items and clear bg on unpainted rects
1232 drawDocumentOnPainter( contentsRect
, &screenPainter
);
1233 // 2) Layer 1: paint opaque selection
1234 if ( !selectionRect
.isNull() && selectionRect
.intersects( contentsRect
) &&
1235 !selectionRectInternal
.contains( contentsRect
) )
1237 screenPainter
.setPen( palette().color( QPalette::Active
, QPalette::Highlight
).dark(110) );
1238 screenPainter
.drawRect( selectionRect
);
1240 // 3) Layer 1: give annotator painting control
1241 if ( d
->annotator
&& d
->annotator
->routePaints( contentsRect
) )
1242 d
->annotator
->routePaint( &screenPainter
, contentsRect
);
1243 // 4) Layer 2: overlays
1244 if ( Okular::Settings::debugDrawBoundaries() )
1246 screenPainter
.setPen( Qt::red
);
1247 screenPainter
.drawRect( contentsRect
);
1253 void PageView::resizeEvent( QResizeEvent
*e
)
1255 if ( d
->items
.isEmpty() )
1257 widget()->resize(e
->size());
1261 // start a timer that will refresh the pixmap after 0.2s
1262 if ( !d
->delayResizeTimer
)
1264 d
->delayResizeTimer
= new QTimer( this );
1265 d
->delayResizeTimer
->setSingleShot( true );
1266 connect( d
->delayResizeTimer
, SIGNAL( timeout() ), this, SLOT( slotRelayoutPages() ) );
1268 d
->delayResizeTimer
->start( 200 );
1271 void PageView::keyPressEvent( QKeyEvent
* e
)
1275 // if performing a selection or dyn zooming, disable keys handling
1276 if ( ( d
->mouseSelecting
&& e
->key() != Qt::Key_Escape
) || d
->mouseMidZooming
)
1279 // if viewport is moving, disable keys handling
1280 if ( d
->viewportMoveActive
)
1283 // move/scroll page by using keys
1287 case Qt::Key_PageDown
:
1290 case Qt::Key_PageUp
:
1291 case Qt::Key_Backspace
:
1292 if ( e
->key() == Qt::Key_Down
1293 || e
->key() == Qt::Key_PageDown
1294 || ( e
->key() == Qt::Key_Space
&& ( e
->modifiers() & Qt::ShiftModifier
) != Qt::ShiftModifier
) )
1296 // if in single page mode and at the bottom of the screen, go to next page
1297 if ( Okular::Settings::viewContinuous() || verticalScrollBar()->value() < verticalScrollBar()->maximum() )
1299 if ( e
->key() == Qt::Key_Down
)
1300 verticalScrollBar()->triggerAction( QScrollBar::SliderSingleStepAdd
);
1302 verticalScrollBar()->triggerAction( QScrollBar::SliderPageStepAdd
);
1304 else if ( (int)d
->document
->currentPage() < d
->items
.count() - 1 )
1306 // more optimized than document->setNextPage and then move view to top
1307 Okular::DocumentViewport newViewport
= d
->document
->viewport();
1308 newViewport
.pageNumber
+= d
->document
->currentPage() ? viewColumns() : 1;
1309 if ( newViewport
.pageNumber
>= (int)d
->items
.count() )
1310 newViewport
.pageNumber
= d
->items
.count() - 1;
1311 newViewport
.rePos
.enabled
= true;
1312 newViewport
.rePos
.normalizedY
= 0.0;
1313 d
->document
->setViewport( newViewport
);
1318 // if in single page mode and at the top of the screen, go to \ page
1319 if ( Okular::Settings::viewContinuous() || verticalScrollBar()->value() > verticalScrollBar()->minimum() )
1321 if ( e
->key() == Qt::Key_Up
)
1322 verticalScrollBar()->triggerAction( QScrollBar::SliderSingleStepSub
);
1324 verticalScrollBar()->triggerAction( QScrollBar::SliderPageStepSub
);
1326 else if ( d
->document
->currentPage() > 0 )
1328 // more optimized than document->setPrevPage and then move view to bottom
1329 Okular::DocumentViewport newViewport
= d
->document
->viewport();
1330 newViewport
.pageNumber
-= viewColumns();
1331 if ( newViewport
.pageNumber
< 0 )
1332 newViewport
.pageNumber
= 0;
1333 newViewport
.rePos
.enabled
= true;
1334 newViewport
.rePos
.normalizedY
= 1.0;
1335 d
->document
->setViewport( newViewport
);
1340 horizontalScrollBar()->triggerAction( QScrollBar::SliderSingleStepSub
);
1343 horizontalScrollBar()->triggerAction( QScrollBar::SliderSingleStepAdd
);
1345 case Qt::Key_Escape
:
1347 d
->mousePressPos
= QPoint();
1348 if ( d
->aPrevAction
)
1350 d
->aPrevAction
->trigger();
1355 case Qt::Key_Control
:
1356 if ( d
->autoScrollTimer
)
1358 if ( d
->autoScrollTimer
->isActive() )
1359 d
->autoScrollTimer
->stop();
1364 // else fall trhough
1369 // if a known key has been pressed, stop scrolling the page
1370 if ( d
->autoScrollTimer
)
1372 d
->scrollIncrement
= 0;
1373 d
->autoScrollTimer
->stop();
1377 void PageView::keyReleaseEvent( QKeyEvent
* e
)
1381 if ( d
->annotator
&& d
->annotator
->routeEvents() )
1383 if ( d
->annotator
->routeKeyEvent( e
) )
1387 if ( e
->key() == Qt::Key_Escape
&& d
->autoScrollTimer
)
1389 d
->scrollIncrement
= 0;
1390 d
->autoScrollTimer
->stop();
1394 void PageView::inputMethodEvent( QInputMethodEvent
* e
)
1399 static QPoint
rotateInRect( const QPoint
&rotated
, Okular::Rotation rotation
)
1405 case Okular::Rotation90
:
1406 ret
= QPoint( rotated
.y(), -rotated
.x() );
1408 case Okular::Rotation180
:
1409 ret
= QPoint( -rotated
.x(), -rotated
.y() );
1411 case Okular::Rotation270
:
1412 ret
= QPoint( -rotated
.y(), rotated
.x() );
1414 case Okular::Rotation0
: // no modifications
1415 default: // other cases
1422 void PageView::contentsMouseMoveEvent( QMouseEvent
* e
)
1424 // don't perform any mouse action when no document is shown
1425 if ( d
->items
.isEmpty() )
1428 // don't perform any mouse action when viewport is autoscrolling
1429 if ( d
->viewportMoveActive
)
1432 // if holding mouse mid button, perform zoom
1433 if ( d
->mouseMidZooming
&& (e
->buttons() & Qt::MidButton
) )
1435 int mouseY
= e
->globalPos().y();
1436 int deltaY
= d
->mouseMidLastY
- mouseY
;
1438 // wrap mouse from top to bottom
1439 QRect mouseContainer
= KGlobalSettings::desktopGeometry( this );
1440 if ( mouseY
<= mouseContainer
.top() + 4 &&
1441 d
->zoomFactor
< 3.99 )
1443 mouseY
= mouseContainer
.bottom() - 5;
1444 QCursor::setPos( e
->globalPos().x(), mouseY
);
1446 // wrap mouse from bottom to top
1447 else if ( mouseY
>= mouseContainer
.bottom() - 4 &&
1448 d
->zoomFactor
> 0.11 )
1450 mouseY
= mouseContainer
.top() + 5;
1451 QCursor::setPos( e
->globalPos().x(), mouseY
);
1453 // remember last position
1454 d
->mouseMidLastY
= mouseY
;
1456 // update zoom level, perform zoom and redraw
1459 d
->zoomFactor
*= ( 1.0 + ( (double)deltaY
/ 500.0 ) );
1460 updateZoom( ZoomRefreshCurrent
);
1461 viewport()->repaint();
1466 // if we're editing an annotation, dispatch event to it
1467 if ( d
->annotator
&& d
->annotator
->routeEvents() )
1469 PageViewItem
* pageItem
= pickItemOnPoint( e
->x(), e
->y() );
1470 d
->annotator
->routeEvent( e
, pageItem
);
1474 bool leftButton
= (e
->buttons() == Qt::LeftButton
);
1475 bool rightButton
= (e
->buttons() == Qt::RightButton
);
1476 switch ( d
->mouseMode
)
1483 PageViewItem
* pageItem
= pickItemOnPoint( e
->x(), e
->y() );
1486 const QRect
& itemRect
= pageItem
->uncroppedGeometry();
1487 QPoint newpos
= QPoint( e
->x(), e
->y() ) - itemRect
.topLeft();
1488 Okular::NormalizedRect r
= d
->mouseAnn
->boundingRectangle();
1489 QPoint
p( newpos
- d
->mouseAnnPos
);
1490 QPointF
pf( rotateInRect( p
, pageItem
->page()->totalOrientation() ) );
1491 if ( pageItem
->page()->totalOrientation() % 2 == 0 )
1493 pf
.rx() /= pageItem
->uncroppedWidth();
1494 pf
.ry() /= pageItem
->uncroppedHeight();
1498 pf
.rx() /= pageItem
->uncroppedHeight();
1499 pf
.ry() /= pageItem
->uncroppedWidth();
1501 d
->mouseAnn
->translate( Okular::NormalizedPoint( pf
.x(), pf
.y() ) );
1502 d
->mouseAnnPos
= newpos
;
1503 d
->document
->modifyPageAnnotation( pageItem
->pageNumber(), d
->mouseAnn
);
1507 else if ( !d
->mouseGrabPos
.isNull() )
1509 QPoint mousePos
= e
->globalPos();
1510 QPoint delta
= d
->mouseGrabPos
- mousePos
;
1512 // wrap mouse from top to bottom
1513 QRect mouseContainer
= KGlobalSettings::desktopGeometry( this );
1514 if ( mousePos
.y() <= mouseContainer
.top() + 4 &&
1515 verticalScrollBar()->value() < verticalScrollBar()->maximum() - 10 )
1517 mousePos
.setY( mouseContainer
.bottom() - 5 );
1518 QCursor::setPos( mousePos
);
1520 // wrap mouse from bottom to top
1521 else if ( mousePos
.y() >= mouseContainer
.bottom() - 4 &&
1522 verticalScrollBar()->value() > 10 )
1524 mousePos
.setY( mouseContainer
.top() + 5 );
1525 QCursor::setPos( mousePos
);
1527 // remember last position
1528 d
->mouseGrabPos
= mousePos
;
1530 // scroll page by position increment
1531 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + delta
.x());
1532 verticalScrollBar()->setValue(verticalScrollBar()->value() + delta
.y());
1535 else if ( rightButton
&& !d
->mousePressPos
.isNull() )
1537 // if mouse moves 5 px away from the press point, switch to 'selection'
1538 int deltaX
= d
->mousePressPos
.x() - e
->globalPos().x(),
1539 deltaY
= d
->mousePressPos
.y() - e
->globalPos().y();
1540 if ( deltaX
> 5 || deltaX
< -5 || deltaY
> 5 || deltaY
< -5 )
1542 d
->aPrevAction
= d
->aMouseNormal
;
1543 d
->aMouseSelect
->trigger();
1544 QPoint
newPos(e
->x() + deltaX
, e
->y() + deltaY
);
1545 selectionStart( newPos
, palette().color( QPalette::Active
, QPalette::Highlight
).light( 120 ), false );
1546 selectionEndPoint( e
->pos() );
1552 // only hovering the page, so update the cursor
1553 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
1559 case MouseImageSelect
:
1560 // set second corner of selection
1561 if ( d
->mouseSelecting
)
1562 selectionEndPoint( e
->pos() );
1564 case MouseTextSelect
:
1565 // if mouse moves 5 px away from the press point and the document soupports text extraction, do 'textselection'
1566 if ( !d
->mouseTextSelecting
&& !d
->mousePressPos
.isNull() && d
->document
->supportsSearching() && ( ( e
->pos() - d
->mouseSelectPos
).manhattanLength() > 5 ) )
1568 d
->mouseTextSelecting
= true;
1570 if ( d
->mouseTextSelecting
)
1573 QList
< Okular::RegularAreaRect
* > selections
= textSelections( e
->pos(), d
->mouseSelectPos
, first
);
1574 QSet
< int > pagesWithSelectionSet
;
1575 for ( int i
= 0; i
< selections
.count(); ++i
)
1576 pagesWithSelectionSet
.insert( i
+ first
);
1578 QSet
< int > noMoreSelectedPages
= d
->pagesWithTextSelection
- pagesWithSelectionSet
;
1579 // clear the selection from pages not selected anymore
1580 foreach( int p
, noMoreSelectedPages
)
1582 d
->document
->setPageTextSelection( p
, 0, QColor() );
1584 // set the new selection for the selected pages
1585 foreach( int p
, pagesWithSelectionSet
)
1587 d
->document
->setPageTextSelection( p
, selections
[ p
- first
], palette().color( QPalette::Active
, QPalette::Highlight
) );
1589 d
->pagesWithTextSelection
= pagesWithSelectionSet
;
1591 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
1596 void PageView::contentsMousePressEvent( QMouseEvent
* e
)
1598 // don't perform any mouse action when no document is shown
1599 if ( d
->items
.isEmpty() )
1602 // if performing a selection or dyn zooming, disable mouse press
1603 if ( d
->mouseSelecting
|| d
->mouseMidZooming
|| d
->viewportMoveActive
)
1606 // if the page is scrolling, stop it
1607 if ( d
->autoScrollTimer
)
1609 d
->scrollIncrement
= 0;
1610 d
->autoScrollTimer
->stop();
1613 // if pressing mid mouse button while not doing other things, begin 'continuous zoom' mode
1614 if ( e
->button() == Qt::MidButton
)
1616 d
->mouseMidZooming
= true;
1617 d
->mouseMidLastY
= e
->globalPos().y();
1618 setCursor( Qt::SizeVerCursor
);
1622 // if we're editing an annotation, dispatch event to it
1623 if ( d
->annotator
&& d
->annotator
->routeEvents() )
1625 PageViewItem
* pageItem
= pickItemOnPoint( e
->x(), e
->y() );
1626 d
->annotator
->routeEvent( e
, pageItem
);
1630 // update press / 'start drag' mouse position
1631 d
->mousePressPos
= e
->globalPos();
1633 // handle mode dependant mouse press actions
1634 bool leftButton
= e
->button() == Qt::LeftButton
,
1635 rightButton
= e
->button() == Qt::RightButton
;
1637 // Not sure we should erase the selection when clicking with left.
1638 if ( d
->mouseMode
!= MouseTextSelect
)
1639 textSelectionClear();
1641 switch ( d
->mouseMode
)
1643 case MouseNormal
: // drag start / click / link following
1646 PageViewItem
* pageItem
= 0;
1647 if ( ( e
->modifiers() & Qt::ControlModifier
) && ( pageItem
= pickItemOnPoint( e
->x(), e
->y() ) ) )
1649 // find out normalized mouse coords inside current item
1650 const QRect
& itemRect
= pageItem
->uncroppedGeometry();
1651 double nX
= pageItem
->absToPageX(e
->x());
1652 double nY
= pageItem
->absToPageY(e
->y());
1653 const Okular::ObjectRect
* orect
= pageItem
->page()->objectRect( Okular::ObjectRect::OAnnotation
, nX
, nY
, itemRect
.width(), itemRect
.height() );
1654 d
->mouseAnnPos
= QPoint( e
->x(), e
->y() ) - itemRect
.topLeft();
1656 d
->mouseAnn
= ( (Okular::AnnotationObjectRect
*)orect
)->annotation();
1657 // consider no annotation caught if its type is not movable
1658 if ( d
->mouseAnn
&& !d
->mouseAnn
->canBeMoved() )
1663 d
->mouseGrabPos
= d
->mouseOnRect
? QPoint() : d
->mousePressPos
;
1664 if ( !d
->mouseOnRect
)
1665 setCursor( Qt::SizeAllCursor
);
1668 else if ( rightButton
)
1670 PageViewItem
* pageItem
= pickItemOnPoint( e
->x(), e
->y() );
1673 // find out normalized mouse coords inside current item
1674 const QRect
& itemRect
= pageItem
->uncroppedGeometry();
1675 double nX
= pageItem
->absToPageX(e
->x());
1676 double nY
= pageItem
->absToPageY(e
->y());
1677 Okular::Annotation
* ann
= 0;
1678 const Okular::ObjectRect
* orect
= pageItem
->page()->objectRect( Okular::ObjectRect::OAnnotation
, nX
, nY
, itemRect
.width(), itemRect
.height() );
1680 ann
= ( (Okular::AnnotationObjectRect
*)orect
)->annotation();
1683 AnnotationPopup
popup( d
->document
, this );
1684 popup
.addAnnotation( ann
, pageItem
->pageNumber() );
1686 connect( &popup
, SIGNAL( setAnnotationWindow( Okular::Annotation
* ) ),
1687 this, SLOT( setAnnotationWindow( Okular::Annotation
* ) ) );
1688 connect( &popup
, SIGNAL( removeAnnotationWindow( Okular::Annotation
* ) ),
1689 this, SLOT( removeAnnotationWindow( Okular::Annotation
* ) ) );
1691 popup
.exec( e
->globalPos() );
1697 case MouseZoom
: // set first corner of the zoom rect
1699 selectionStart( e
->pos(), palette().color( QPalette::Active
, QPalette::Highlight
), false );
1700 else if ( rightButton
)
1701 updateZoom( ZoomOut
);
1704 case MouseSelect
: // set first corner of the selection rect
1705 case MouseImageSelect
:
1708 selectionStart( e
->pos(), palette().color( QPalette::Active
, QPalette::Highlight
).light( 120 ), false );
1711 case MouseTextSelect
:
1712 d
->mouseSelectPos
= e
->pos();
1715 textSelectionClear();
1721 void PageView::contentsMouseReleaseEvent( QMouseEvent
* e
)
1723 // stop the drag scrolling
1724 d
->dragScrollTimer
.stop();
1726 // don't perform any mouse action when no document is shown..
1727 if ( d
->items
.isEmpty() )
1729 // ..except for right Clicks (emitted even it viewport is empty)
1730 if ( e
->button() == Qt::RightButton
)
1731 emit
rightClick( 0, e
->globalPos() );
1735 // don't perform any mouse action when viewport is autoscrolling
1736 if ( d
->viewportMoveActive
)
1739 // handle mode indepent mid buttom zoom
1740 if ( d
->mouseMidZooming
&& (e
->button() == Qt::MidButton
) )
1742 d
->mouseMidZooming
= false;
1743 // request pixmaps since it was disabled during drag
1744 slotRequestVisiblePixmaps();
1745 // the cursor may now be over a link.. update it
1746 updateCursor( e
->pos() );
1750 // if we're editing an annotation, dispatch event to it
1751 if ( d
->annotator
&& d
->annotator
->routeEvents() )
1753 PageViewItem
* pageItem
= pickItemOnPoint( e
->x(), e
->y() );
1754 d
->annotator
->routeEvent( e
, pageItem
);
1760 setCursor( Qt::ArrowCursor
);
1764 bool leftButton
= e
->button() == Qt::LeftButton
;
1765 bool rightButton
= e
->button() == Qt::RightButton
;
1766 switch ( d
->mouseMode
)
1769 // return the cursor to its normal state after dragging
1770 if ( cursor().shape() == Qt::SizeAllCursor
)
1771 updateCursor( e
->pos() );
1773 PageViewItem
* pageItem
= pickItemOnPoint( e
->x(), e
->y() );
1775 // if the mouse has not moved since the press, that's a -click-
1776 if ( leftButton
&& pageItem
&& d
->mousePressPos
== e
->globalPos())
1778 double nX
= pageItem
->absToPageX(e
->x());
1779 double nY
= pageItem
->absToPageY(e
->y());
1780 const Okular::ObjectRect
* rect
;
1781 rect
= pageItem
->page()->objectRect( Okular::ObjectRect::Action
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
1784 // handle click over a link
1785 const Okular::Action
* action
= static_cast< const Okular::Action
* >( rect
->object() );
1786 d
->document
->processAction( action
);
1790 // TODO: find a better way to activate the source reference "links"
1791 // for the moment they are activated with Shift + left click
1792 rect
= e
->modifiers() == Qt::ShiftModifier
? pageItem
->page()->objectRect( Okular::ObjectRect::SourceRef
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() ) : 0;
1795 const Okular::SourceReference
* ref
= static_cast< const Okular::SourceReference
* >( rect
->object() );
1796 d
->document
->processSourceReference( ref
);
1799 // a link can move us to another page or even to another document, there's no point in trying to
1800 // process the click on the image once we have processes the click on the link
1801 rect
= pageItem
->page()->objectRect( Okular::ObjectRect::Image
, nX
, nY
, pageItem
->width(), pageItem
->height() );
1804 // handle click over a image
1806 /* Enrico and me have decided this is not worth the trouble it generates
1809 // if not on a rect, the click selects the page
1810 // if ( pageItem->pageNumber() != (int)d->document->currentPage() )
1811 d->document->setViewportPage( pageItem->pageNumber(), PAGEVIEW_ID );
1816 else if ( rightButton
)
1818 if ( pageItem
&& d
->mousePressPos
== e
->globalPos() )
1820 double nX
= pageItem
->absToPageX(e
->x());
1821 double nY
= pageItem
->absToPageY(e
->y());
1822 const Okular::ObjectRect
* rect
;
1823 rect
= pageItem
->page()->objectRect( Okular::ObjectRect::Action
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
1826 // handle right click over a link
1827 const Okular::Action
* link
= static_cast< const Okular::Action
* >( rect
->object() );
1828 // creating the menu and its actions
1830 QAction
* actProcessLink
= menu
.addAction( i18n( "Follow This Link" ) );
1831 QAction
* actCopyLinkLocation
= 0;
1832 if ( dynamic_cast< const Okular::BrowseAction
* >( link
) )
1833 actCopyLinkLocation
= menu
.addAction( KIcon( "edit-copy" ), i18n( "Copy Link Address" ) );
1834 QAction
* res
= menu
.exec( e
->globalPos() );
1837 if ( res
== actProcessLink
)
1839 d
->document
->processAction( link
);
1841 else if ( res
== actCopyLinkLocation
)
1843 const Okular::BrowseAction
* browseLink
= static_cast< const Okular::BrowseAction
* >( link
);
1844 QClipboard
*cb
= QApplication::clipboard();
1845 cb
->setText( browseLink
->url(), QClipboard::Clipboard
);
1846 if ( cb
->supportsSelection() )
1847 cb
->setText( browseLink
->url(), QClipboard::Selection
);
1853 // a link can move us to another page or even to another document, there's no point in trying to
1854 // process the click on the image once we have processes the click on the link
1855 rect
= pageItem
->page()->objectRect( Okular::ObjectRect::Image
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
1858 // handle right click over a image
1862 // right click (if not within 5 px of the press point, the mode
1863 // had been already changed to 'Selection' instead of 'Normal')
1864 emit
rightClick( pageItem
->page(), e
->globalPos() );
1870 // right click (if not within 5 px of the press point, the mode
1871 // had been already changed to 'Selection' instead of 'Normal')
1872 emit
rightClick( pageItem
? pageItem
->page() : 0, e
->globalPos() );
1878 // if a selection rect has been defined, zoom into it
1879 if ( leftButton
&& d
->mouseSelecting
)
1881 QRect selRect
= d
->mouseSelectionRect
.normalized();
1882 if ( selRect
.width() <= 8 && selRect
.height() <= 8 )
1888 // find out new zoom ratio and normalized view center (relative to the contentsRect)
1889 double zoom
= qMin( (double)viewport()->width() / (double)selRect
.width(), (double)viewport()->height() / (double)selRect
.height() );
1890 double nX
= (double)(selRect
.left() + selRect
.right()) / (2.0 * (double)widget()->width());
1891 double nY
= (double)(selRect
.top() + selRect
.bottom()) / (2.0 * (double)widget()->height());
1894 if ( d
->zoomFactor
<= 4.0 || zoom
<= 1.0 )
1896 d
->zoomFactor
*= zoom
;
1897 viewport()->setUpdatesEnabled( false );
1898 updateZoom( ZoomRefreshCurrent
);
1899 viewport()->setUpdatesEnabled( true );
1902 // recenter view and update the viewport
1903 center( (int)(nX
* widget()->width()), (int)(nY
* widget()->height()) );
1906 // hide message box and delete overlay window
1912 case MouseImageSelect
:
1914 // if mouse is released and selection is null this is a rightClick
1915 if ( rightButton
&& !d
->mouseSelecting
)
1917 PageViewItem
* pageItem
= pickItemOnPoint( e
->x(), e
->y() );
1918 emit
rightClick( pageItem
? pageItem
->page() : 0, e
->globalPos() );
1922 // if a selection is defined, display a popup
1923 if ( (!leftButton
&& !d
->aPrevAction
) || (!rightButton
&& d
->aPrevAction
) ||
1924 !d
->mouseSelecting
)
1927 QRect selectionRect
= d
->mouseSelectionRect
.normalized();
1928 if ( selectionRect
.width() <= 8 && selectionRect
.height() <= 8 )
1931 if ( d
->aPrevAction
)
1933 d
->aPrevAction
->trigger();
1939 // if we support text generation
1940 QString selectedText
;
1941 if (d
->document
->supportsSearching())
1943 // grab text in selection by extracting it from all intersected pages
1944 const Okular::Page
* okularPage
=0;
1945 QVector
< PageViewItem
* >::const_iterator iIt
= d
->items
.begin(), iEnd
= d
->items
.end();
1946 for ( ; iIt
!= iEnd
; ++iIt
)
1948 PageViewItem
* item
= *iIt
;
1949 if ( !item
->isVisible() )
1952 const QRect
& itemRect
= item
->croppedGeometry();
1953 if ( selectionRect
.intersects( itemRect
) )
1955 // request the textpage if there isn't one
1956 okularPage
= item
->page();
1957 kWarning() << "checking if page" << item
->pageNumber() << "has text:" << okularPage
->hasTextPage();
1958 if ( !okularPage
->hasTextPage() )
1959 d
->document
->requestTextPage( okularPage
->number() );
1960 // grab text in the rect that intersects itemRect
1961 QRect relativeRect
= selectionRect
.intersect( itemRect
);
1962 relativeRect
.translate( -item
->uncroppedGeometry().topLeft() );
1963 Okular::RegularAreaRect rects
;
1964 rects
.append( Okular::NormalizedRect( relativeRect
, item
->uncroppedWidth(), item
->uncroppedHeight() ) );
1965 selectedText
+= okularPage
->text( &rects
);
1970 // popup that ask to copy:text and copy/save:image
1972 QAction
*textToClipboard
= 0, *speakText
= 0, *imageToClipboard
= 0, *imageToFile
= 0;
1973 if ( d
->document
->supportsSearching() && !selectedText
.isEmpty() )
1975 menu
.addTitle( i18np( "Text (1 character)", "Text (%1 characters)", selectedText
.length() ) );
1976 textToClipboard
= menu
.addAction( KIcon("edit-copy"), i18n( "Copy to Clipboard" ) );
1977 if ( !d
->document
->isAllowed( Okular::AllowCopy
) )
1979 textToClipboard
->setEnabled( false );
1980 textToClipboard
->setText( i18n("Copy forbidden by DRM") );
1982 if ( Okular::Settings::useKTTSD() )
1983 speakText
= menu
.addAction( KIcon("text-speak"), i18n( "Speak Text" ) );
1985 menu
.addTitle( i18n( "Image (%1 by %2 pixels)", selectionRect
.width(), selectionRect
.height() ) );
1986 imageToClipboard
= menu
.addAction( KIcon("image-x-generic"), i18n( "Copy to Clipboard" ) );
1987 imageToFile
= menu
.addAction( KIcon("document-save"), i18n( "Save to File..." ) );
1988 QAction
*choice
= menu
.exec( e
->globalPos() );
1989 // check if the user really selected an action
1992 // IMAGE operation chosen
1993 if ( choice
== imageToClipboard
|| choice
== imageToFile
)
1995 // renders page into a pixmap
1996 QPixmap
copyPix( selectionRect
.width(), selectionRect
.height() );
1997 QPainter
copyPainter( ©Pix
);
1998 copyPainter
.translate( -selectionRect
.left(), -selectionRect
.top() );
1999 drawDocumentOnPainter( selectionRect
, ©Painter
);
2002 if ( choice
== imageToClipboard
)
2004 // [2] copy pixmap to clipboard
2005 QClipboard
*cb
= QApplication::clipboard();
2006 cb
->setPixmap( copyPix
, QClipboard::Clipboard
);
2007 if ( cb
->supportsSelection() )
2008 cb
->setPixmap( copyPix
, QClipboard::Selection
);
2009 d
->messageWindow
->display( i18n( "Image [%1x%2] copied to clipboard.", copyPix
.width(), copyPix
.height() ) );
2011 else if ( choice
== imageToFile
)
2013 // [3] save pixmap to file
2014 QString fileName
= KFileDialog::getSaveFileName( KUrl(), "image/png image/jpeg", this );
2015 if ( fileName
.isEmpty() )
2016 d
->messageWindow
->display( i18n( "File not saved." ), PageViewMessage::Warning
);
2019 KMimeType::Ptr mime
= KMimeType::findByUrl( fileName
);
2024 type
= mime
->name().section( '/', -1 ).toUpper();
2025 copyPix
.save( fileName
, qPrintable( type
) );
2026 d
->messageWindow
->display( i18n( "Image [%1x%2] saved to %3 file.", copyPix
.width(), copyPix
.height(), type
) );
2030 // TEXT operation chosen
2033 if ( choice
== textToClipboard
)
2035 // [1] copy text to clipboard
2036 QClipboard
*cb
= QApplication::clipboard();
2037 cb
->setText( selectedText
, QClipboard::Clipboard
);
2038 if ( cb
->supportsSelection() )
2039 cb
->setText( selectedText
, QClipboard::Selection
);
2041 else if ( choice
== speakText
)
2043 // [2] speech selection using KTTSD
2044 d
->tts()->say( selectedText
);
2048 // clear widget selection and invalidate rect
2051 // restore previous action if came from it using right button
2052 if ( d
->aPrevAction
)
2054 d
->aPrevAction
->trigger();
2058 case MouseTextSelect
:
2059 setCursor( Qt::ArrowCursor
);
2060 if ( d
->mouseTextSelecting
)
2062 d
->mouseTextSelecting
= false;
2063 // textSelectionClear();
2064 if ( d
->document
->isAllowed( Okular::AllowCopy
) )
2066 const QString text
= d
->selectedText();
2067 if ( !text
.isEmpty() )
2069 QClipboard
*cb
= QApplication::clipboard();
2070 if ( cb
->supportsSelection() )
2071 cb
->setText( text
, QClipboard::Selection
);
2075 else if ( !d
->mousePressPos
.isNull() && rightButton
)
2078 QAction
*textToClipboard
= menu
.addAction( KIcon( "edit-copy" ), i18n( "Copy Text" ) );
2079 QAction
*speakText
= 0;
2080 if ( Okular::Settings::useKTTSD() )
2081 speakText
= menu
.addAction( KIcon( "text-speak" ), i18n( "Speak Text" ) );
2082 if ( !d
->document
->isAllowed( Okular::AllowCopy
) )
2084 textToClipboard
->setEnabled( false );
2085 textToClipboard
->setText( i18n("Copy forbidden by DRM") );
2087 QAction
*choice
= menu
.exec( e
->globalPos() );
2088 // check if the user really selected an action
2091 if ( choice
== textToClipboard
)
2092 copyTextSelection();
2093 else if ( choice
== speakText
)
2095 const QString text
= d
->selectedText();
2096 d
->tts()->say( text
);
2103 // reset mouse press / 'drag start' position
2104 d
->mousePressPos
= QPoint();
2107 void PageView::wheelEvent( QWheelEvent
*e
)
2109 // don't perform any mouse action when viewport is autoscrolling
2110 if ( d
->viewportMoveActive
)
2113 if ( !d
->document
->isOpened() )
2115 QScrollArea::wheelEvent( e
);
2119 int delta
= e
->delta(),
2120 vScroll
= verticalScrollBar()->value();
2122 if ( (e
->modifiers() & Qt::ControlModifier
) == Qt::ControlModifier
) {
2123 if ( e
->delta() < 0 )
2128 else if ( delta
<= -120 && !Okular::Settings::viewContinuous() && vScroll
== verticalScrollBar()->maximum() )
2131 if ( (int)d
->document
->currentPage() < d
->items
.count() - 1 )
2133 // more optimized than document->setNextPage and then move view to top
2134 Okular::DocumentViewport newViewport
= d
->document
->viewport();
2135 newViewport
.pageNumber
+= d
->document
->currentPage() ? viewColumns() : 1;
2136 if ( newViewport
.pageNumber
>= (int)d
->items
.count() )
2137 newViewport
.pageNumber
= d
->items
.count() - 1;
2138 newViewport
.rePos
.enabled
= true;
2139 newViewport
.rePos
.normalizedY
= 0.0;
2140 d
->document
->setViewport( newViewport
);
2143 else if ( delta
>= 120 && !Okular::Settings::viewContinuous() && vScroll
== verticalScrollBar()->minimum() )
2146 if ( d
->document
->currentPage() > 0 )
2148 // more optimized than document->setPrevPage and then move view to bottom
2149 Okular::DocumentViewport newViewport
= d
->document
->viewport();
2150 newViewport
.pageNumber
-= viewColumns();
2151 if ( newViewport
.pageNumber
< 0 )
2152 newViewport
.pageNumber
= 0;
2153 newViewport
.rePos
.enabled
= true;
2154 newViewport
.rePos
.normalizedY
= 1.0;
2155 d
->document
->setViewport( newViewport
);
2159 QScrollArea::wheelEvent( e
);
2161 QPoint cp
= widget()->mapFromGlobal(mapToGlobal(e
->pos()));
2165 void PageView::dragEnterEvent( QDragEnterEvent
* ev
)
2170 void PageView::dragMoveEvent( QDragMoveEvent
* ev
)
2175 void PageView::dropEvent( QDropEvent
* ev
)
2177 if ( KUrl::List::canDecode( ev
->mimeData() ) )
2178 emit
urlDropped( KUrl::List::fromMimeData( ev
->mimeData() ).first() );
2182 QList
< Okular::RegularAreaRect
* > PageView::textSelections( const QPoint
& start
, const QPoint
& end
, int& firstpage
)
2185 QList
< Okular::RegularAreaRect
* > ret
;
2186 QSet
< int > affectedItemsSet
;
2187 QRect selectionRect
= QRect( start
, end
).normalized();
2188 foreach( PageViewItem
* item
, d
->items
)
2190 if ( item
->isVisible() && selectionRect
.intersects( item
->croppedGeometry() ) )
2191 affectedItemsSet
.insert( item
->pageNumber() );
2193 #ifdef PAGEVIEW_DEBUG
2194 kDebug() << ">>>> item selected by mouse:" << affectedItemsSet
.count();
2197 if ( !affectedItemsSet
.isEmpty() )
2199 // is the mouse drag line the ne-sw diagonal of the selection rect?
2200 bool direction_ne_sw
= start
== selectionRect
.topRight() || start
== selectionRect
.bottomLeft();
2202 int tmpmin
= d
->document
->pages();
2204 foreach( int p
, affectedItemsSet
)
2206 if ( p
< tmpmin
) tmpmin
= p
;
2207 if ( p
> tmpmax
) tmpmax
= p
;
2210 PageViewItem
* a
= pickItemOnPoint( (int)( direction_ne_sw
? selectionRect
.right() : selectionRect
.left() ), (int)selectionRect
.top() );
2211 int min
= a
&& ( a
->pageNumber() != tmpmax
) ? a
->pageNumber() : tmpmin
;
2212 PageViewItem
* b
= pickItemOnPoint( (int)( direction_ne_sw
? selectionRect
.left() : selectionRect
.right() ), (int)selectionRect
.bottom() );
2213 int max
= b
&& ( b
->pageNumber() != tmpmin
) ? b
->pageNumber() : tmpmax
;
2215 QList
< int > affectedItemsIds
;
2216 for ( int i
= min
; i
<= max
; ++i
)
2217 affectedItemsIds
.append( i
);
2218 #ifdef PAGEVIEW_DEBUG
2219 kDebug() << ">>>> pages:" << affectedItemsIds
;
2221 firstpage
= affectedItemsIds
.first();
2223 if ( affectedItemsIds
.count() == 1 )
2225 PageViewItem
* item
= d
->items
[ affectedItemsIds
.first() ];
2226 selectionRect
.translate( -item
->uncroppedGeometry().topLeft() );
2227 ret
.append( textSelectionForItem( item
,
2228 direction_ne_sw
? selectionRect
.topRight() : selectionRect
.topLeft(),
2229 direction_ne_sw
? selectionRect
.bottomLeft() : selectionRect
.bottomRight() ) );
2231 else if ( affectedItemsIds
.count() > 1 )
2234 PageViewItem
* first
= d
->items
[ affectedItemsIds
.first() ];
2235 QRect geom
= first
->croppedGeometry().intersect( selectionRect
).translated( -first
->uncroppedGeometry().topLeft() );
2236 ret
.append( textSelectionForItem( first
,
2237 selectionRect
.bottom() > geom
.height() ? ( direction_ne_sw
? geom
.topRight() : geom
.topLeft() ) : ( direction_ne_sw
? geom
.bottomRight() : geom
.bottomLeft() ),
2240 PageViewItem
* last
= d
->items
[ affectedItemsIds
.last() ];
2241 geom
= last
->croppedGeometry().intersect( selectionRect
).translated( -last
->uncroppedGeometry().topLeft() );
2242 // the last item needs to appended at last...
2243 Okular::RegularAreaRect
* lastArea
= textSelectionForItem( last
,
2245 selectionRect
.bottom() > geom
.height() ? ( direction_ne_sw
? geom
.bottomLeft() : geom
.bottomRight() ) : ( direction_ne_sw
? geom
.topLeft() : geom
.topRight() ) );
2246 affectedItemsIds
.removeFirst();
2247 affectedItemsIds
.removeLast();
2248 // item between the two above
2249 foreach( int page
, affectedItemsIds
)
2251 ret
.append( textSelectionForItem( d
->items
[ page
] ) );
2253 ret
.append( lastArea
);
2260 void PageView::drawDocumentOnPainter( const QRect
& contentsRect
, QPainter
* p
)
2262 // when checking if an Item is contained in contentsRect, instead of
2263 // growing PageViewItems rects (for keeping outline into account), we
2264 // grow the contentsRect
2265 QRect checkRect
= contentsRect
;
2266 checkRect
.adjust( -3, -3, 1, 1 );
2268 // create a region from which we'll subtract painted rects
2269 QRegion
remainingArea( contentsRect
);
2271 // iterate over all items painting the ones intersecting contentsRect
2272 QVector
< PageViewItem
* >::const_iterator iIt
= d
->items
.begin(), iEnd
= d
->items
.end();
2273 for ( ; iIt
!= iEnd
; ++iIt
)
2275 // check if a piece of the page intersects the contents rect
2276 if ( !(*iIt
)->isVisible() || !(*iIt
)->croppedGeometry().intersects( checkRect
) )
2279 // get item and item's outline geometries
2280 PageViewItem
* item
= *iIt
;
2281 QRect itemGeometry
= item
->croppedGeometry(),
2282 outlineGeometry
= itemGeometry
;
2283 outlineGeometry
.adjust( -1, -1, 3, 3 );
2285 // move the painter to the top-left corner of the real page
2287 p
->translate( itemGeometry
.left(), itemGeometry
.top() );
2289 // draw the page outline (black border and 2px bottom-right shadow)
2290 if ( !itemGeometry
.contains( contentsRect
) )
2292 int itemWidth
= itemGeometry
.width(),
2293 itemHeight
= itemGeometry
.height();
2294 // draw simple outline
2295 p
->setPen( Qt::black
);
2296 p
->drawRect( -1, -1, itemWidth
+ 1, itemHeight
+ 1 );
2297 // draw bottom/right gradient
2298 static int levels
= 2;
2299 int r
= QColor(Qt::gray
).red() / (levels
+ 2),
2300 g
= QColor(Qt::gray
).green() / (levels
+ 2),
2301 b
= QColor(Qt::gray
).blue() / (levels
+ 2);
2302 for ( int i
= 0; i
< levels
; i
++ )
2304 p
->setPen( QColor( r
* (i
+2), g
* (i
+2), b
* (i
+2) ) );
2305 p
->drawLine( i
, i
+ itemHeight
+ 1, i
+ itemWidth
+ 1, i
+ itemHeight
+ 1 );
2306 p
->drawLine( i
+ itemWidth
+ 1, i
, i
+ itemWidth
+ 1, i
+ itemHeight
);
2307 p
->setPen( Qt::gray
);
2308 p
->drawLine( -1, i
+ itemHeight
+ 1, i
- 1, i
+ itemHeight
+ 1 );
2309 p
->drawLine( i
+ itemWidth
+ 1, -1, i
+ itemWidth
+ 1, i
- 1 );
2313 // draw the page using the PagePainter with all flags active
2314 if ( contentsRect
.intersects( itemGeometry
) )
2316 QRect pixmapRect
= contentsRect
.intersect( itemGeometry
);
2317 pixmapRect
.translate( -item
->croppedGeometry().topLeft() );
2318 PagePainter::paintCroppedPageOnPainter( p
, item
->page(), PAGEVIEW_ID
, pageflags
,
2319 item
->uncroppedWidth(), item
->uncroppedHeight(), pixmapRect
,
2323 // remove painted area from 'remainingArea' and restore painter
2324 remainingArea
-= outlineGeometry
.intersect( contentsRect
);
2328 // fill with background color the unpainted area
2329 const QVector
<QRect
> &backRects
= remainingArea
.rects();
2330 int backRectsNumber
= backRects
.count();
2331 // the previous color here was Qt::gray
2332 QColor backColor
= widget()->palette().color( QPalette::Dark
);
2333 for ( int jr
= 0; jr
< backRectsNumber
; jr
++ )
2334 p
->fillRect( backRects
[ jr
], backColor
);
2337 void PageView::updateItemSize( PageViewItem
* item
, int colWidth
, int rowHeight
)
2339 const Okular::Page
* okularPage
= item
->page();
2340 double width
= okularPage
->width(),
2341 height
= okularPage
->height(),
2342 zoom
= d
->zoomFactor
;
2343 Okular::NormalizedRect
crop( 0., 0., 1., 1. );
2346 if ( Okular::Settings::trimMargins() && okularPage
->isBoundingBoxKnown()
2347 && !okularPage
->boundingBox().isNull() )
2349 crop
= okularPage
->boundingBox();
2351 // Rotate the bounding box from upright Rotation0 to current page orientation:
2352 for ( int i
= okularPage
->totalOrientation(); i
> 0; --i
)
2354 Okular::NormalizedRect rot
= crop
;
2355 crop
.left
= 1 - rot
.bottom
;
2356 crop
.top
= rot
.left
;
2357 crop
.right
= 1 - rot
.top
;
2358 crop
.bottom
= rot
.right
;
2361 // Expand the crop slightly beyond the bounding box
2362 static const double cropExpandRatio
= 0.04;
2363 double cropExpand
= cropExpandRatio
* ( (crop
.right
-crop
.left
) + (crop
.bottom
-crop
.top
) ) / 2;
2364 crop
= Okular::NormalizedRect(
2365 crop
.left
- cropExpand
,
2366 crop
.top
- cropExpand
,
2367 crop
.right
+ cropExpand
,
2368 crop
.bottom
+ cropExpand
) & Okular::NormalizedRect( 0, 0, 1, 1 );
2370 // We currently generate a larger image and then crop it, so if the
2371 // crop rect is very small the generated image is huge. Hence, we shouldn't
2372 // let the crop rect become too small.
2373 // Make sure we crop by at most 50% in either dimension:
2374 static const double minCropRatio
= 0.5;
2375 if ( ( crop
.right
- crop
.left
) < minCropRatio
)
2377 double newLeft
= ( crop
.left
+ crop
.right
) / 2 - minCropRatio
/2;
2378 crop
.left
= qMax( 0.0, qMin( 1.0 - minCropRatio
, newLeft
) );
2379 crop
.right
= crop
.left
+ minCropRatio
;
2381 if ( ( crop
.bottom
- crop
.top
) < minCropRatio
)
2383 double newTop
= ( crop
.top
+ crop
.bottom
) / 2 - minCropRatio
/2;
2384 crop
.top
= qMax( 0.0, qMin( 1.0 - minCropRatio
, newTop
) );
2385 crop
.bottom
= crop
.top
+ minCropRatio
;
2388 width
*= ( crop
.right
- crop
.left
);
2389 height
*= ( crop
.bottom
- crop
.top
);
2390 #ifdef PAGEVIEW_DEBUG
2391 kDebug() << "Cropped page" << okularPage
->number() << "to" << crop
2392 << "width" << width
<< "height" << height
<< "by bbox" << okularPage
->boundingBox();
2396 if ( d
->zoomMode
== ZoomFixed
)
2400 item
->setWHZC( (int)width
, (int)height
, d
->zoomFactor
, crop
);
2402 else if ( d
->zoomMode
== ZoomFitWidth
)
2404 height
= ( height
/ width
) * colWidth
;
2405 zoom
= (double)colWidth
/ width
;
2406 item
->setWHZC( colWidth
, (int)height
, zoom
, crop
);
2407 d
->zoomFactor
= zoom
;
2409 else if ( d
->zoomMode
== ZoomFitPage
)
2411 double scaleW
= (double)colWidth
/ (double)width
;
2412 double scaleH
= (double)rowHeight
/ (double)height
;
2413 zoom
= qMin( scaleW
, scaleH
);
2414 item
->setWHZC( (int)(zoom
* width
), (int)(zoom
* height
), zoom
, crop
);
2415 d
->zoomFactor
= zoom
;
2419 kDebug() << "calling updateItemSize with unrecognized d->zoomMode!";
2423 PageViewItem
* PageView::pickItemOnPoint( int x
, int y
)
2425 PageViewItem
* item
= 0;
2426 QLinkedList
< PageViewItem
* >::const_iterator iIt
= d
->visibleItems
.constBegin(), iEnd
= d
->visibleItems
.constEnd();
2427 for ( ; iIt
!= iEnd
; ++iIt
)
2429 PageViewItem
* i
= *iIt
;
2430 const QRect
& r
= i
->croppedGeometry();
2431 if ( x
< r
.right() && x
> r
.left() && y
< r
.bottom() )
2441 void PageView::textSelectionClear()
2443 // something to clear
2444 if ( !d
->pagesWithTextSelection
.isEmpty() )
2446 QSet
< int >::ConstIterator it
= d
->pagesWithTextSelection
.constBegin(), itEnd
= d
->pagesWithTextSelection
.constEnd();
2447 for ( ; it
!= itEnd
; ++it
)
2448 d
->document
->setPageTextSelection( *it
, 0, QColor() );
2449 d
->pagesWithTextSelection
.clear();
2453 void PageView::selectionStart( const QPoint
& pos
, const QColor
& color
, bool /*aboveAll*/ )
2456 d
->mouseSelecting
= true;
2457 d
->mouseSelectionRect
.setRect( pos
.x(), pos
.y(), 1, 1 );
2458 d
->mouseSelectionColor
= color
;
2459 // ensures page doesn't scroll
2460 if ( d
->autoScrollTimer
)
2462 d
->scrollIncrement
= 0;
2463 d
->autoScrollTimer
->stop();
2467 void PageView::selectionEndPoint( const QPoint
& pos
)
2469 if ( !d
->mouseSelecting
)
2472 if (pos
.x() < horizontalScrollBar()->value()) d
->dragScrollVector
.setX(pos
.x() - horizontalScrollBar()->value());
2473 else if (horizontalScrollBar()->value() + viewport()->width() < pos
.x()) d
->dragScrollVector
.setX(pos
.x() - horizontalScrollBar()->value() - viewport()->width());
2474 else d
->dragScrollVector
.setX(0);
2476 if (pos
.y() < verticalScrollBar()->value()) d
->dragScrollVector
.setY(pos
.y() - verticalScrollBar()->value());
2477 else if (verticalScrollBar()->value() + viewport()->height() < pos
.y()) d
->dragScrollVector
.setY(pos
.y() - verticalScrollBar()->value() - viewport()->height());
2478 else d
->dragScrollVector
.setY(0);
2480 if (d
->dragScrollVector
!= QPoint(0, 0))
2482 if (!d
->dragScrollTimer
.isActive()) d
->dragScrollTimer
.start(100);
2484 else d
->dragScrollTimer
.stop();
2486 // update the selection rect
2487 QRect updateRect
= d
->mouseSelectionRect
;
2488 d
->mouseSelectionRect
.setBottomLeft( pos
);
2489 updateRect
|= d
->mouseSelectionRect
;
2490 widget()->update( updateRect
.adjusted( -1, -1, 1, 1 ) );
2493 static Okular::NormalizedPoint
rotateInNormRect( const QPoint
&rotated
, const QRect
&rect
, Okular::Rotation rotation
)
2495 Okular::NormalizedPoint ret
;
2499 case Okular::Rotation0
:
2500 ret
= Okular::NormalizedPoint( rotated
.x(), rotated
.y(), rect
.width(), rect
.height() );
2502 case Okular::Rotation90
:
2503 ret
= Okular::NormalizedPoint( rotated
.y(), rect
.width() - rotated
.x(), rect
.height(), rect
.width() );
2505 case Okular::Rotation180
:
2506 ret
= Okular::NormalizedPoint( rect
.width() - rotated
.x(), rect
.height() - rotated
.y(), rect
.width(), rect
.height() );
2508 case Okular::Rotation270
:
2509 ret
= Okular::NormalizedPoint( rect
.height() - rotated
.y(), rotated
.x(), rect
.height(), rect
.width() );
2516 Okular::RegularAreaRect
* PageView::textSelectionForItem( PageViewItem
* item
, const QPoint
& startPoint
, const QPoint
& endPoint
)
2518 const QRect
& geometry
= item
->uncroppedGeometry();
2519 Okular::NormalizedPoint
startCursor( 0.0, 0.0 );
2520 if ( !startPoint
.isNull() )
2522 startCursor
= rotateInNormRect( startPoint
, geometry
, item
->page()->rotation() );
2524 Okular::NormalizedPoint
endCursor( 1.0, 1.0 );
2525 if ( !endPoint
.isNull() )
2527 endCursor
= rotateInNormRect( endPoint
, geometry
, item
->page()->rotation() );
2529 Okular::TextSelection
mouseTextSelectionInfo( startCursor
, endCursor
);
2531 const Okular::Page
* okularPage
= item
->page();
2533 if ( !okularPage
->hasTextPage() )
2534 d
->document
->requestTextPage( okularPage
->number() );
2536 Okular::RegularAreaRect
* selectionArea
= okularPage
->textArea( &mouseTextSelectionInfo
);
2537 #ifdef PAGEVIEW_DEBUG
2538 kDebug().nospace() << "text areas (" << okularPage
->number() << "): " << ( selectionArea
? QString::number( selectionArea
->count() ) : "(none)" );
2540 return selectionArea
;
2543 void PageView::selectionClear()
2545 QRect updatedRect
= d
->mouseSelectionRect
.normalized().adjusted( 0, 0, 1, 1 );
2546 d
->mouseSelecting
= false;
2547 d
->mouseSelectionRect
.setCoords( 0, 0, 0, 0 );
2548 widget()->update( updatedRect
);
2551 void PageView::updateZoom( ZoomMode newZoomMode
)
2553 if ( newZoomMode
== ZoomFixed
)
2555 if ( d
->aZoom
->currentItem() == 0 )
2556 newZoomMode
= ZoomFitWidth
;
2557 else if ( d
->aZoom
->currentItem() == 1 )
2558 newZoomMode
= ZoomFitPage
;
2561 float newFactor
= d
->zoomFactor
;
2562 QAction
* checkedZoomAction
= 0;
2563 switch ( newZoomMode
)
2565 case ZoomFixed
:{ //ZoomFixed case
2566 QString z
= d
->aZoom
->currentText();
2567 // kdelibs4 sometimes adds accelerators to actions' text directly :(
2570 newFactor
= KGlobal::locale()->readNumber( z
) / 100.0;
2573 newFactor
+= (newFactor
> 0.99) ? ( newFactor
> 1.99 ? 0.5 : 0.2 ) : 0.1;
2574 newZoomMode
= ZoomFixed
;
2577 newFactor
-= (newFactor
> 1.01) ? ( newFactor
> 2.01 ? 0.5 : 0.2 ) : 0.1;
2578 newZoomMode
= ZoomFixed
;
2581 checkedZoomAction
= d
->aZoomFitWidth
;
2584 checkedZoomAction
= d
->aZoomFitPage
;
2587 checkedZoomAction
= d
->aZoomFitText
;
2589 case ZoomRefreshCurrent
:
2590 newZoomMode
= ZoomFixed
;
2594 if ( newFactor
> 4.0 )
2596 if ( newFactor
< 0.1 )
2599 if ( newZoomMode
!= d
->zoomMode
|| (newZoomMode
== ZoomFixed
&& newFactor
!= d
->zoomFactor
) )
2601 // rebuild layout and update the whole viewport
2602 d
->zoomMode
= newZoomMode
;
2603 d
->zoomFactor
= newFactor
;
2604 // be sure to block updates to document's viewport
2605 bool prevState
= d
->blockViewport
;
2606 d
->blockViewport
= true;
2607 slotRelayoutPages();
2608 d
->blockViewport
= prevState
;
2610 slotRequestVisiblePixmaps();
2613 // update actions checked state
2614 if ( d
->aZoomFitWidth
)
2616 d
->aZoomFitWidth
->setChecked( checkedZoomAction
== d
->aZoomFitWidth
);
2617 d
->aZoomFitPage
->setChecked( checkedZoomAction
== d
->aZoomFitPage
);
2618 // d->aZoomFitText->setChecked( checkedZoomAction == d->aZoomFitText );
2622 d
->aZoomIn
->setEnabled( d
->zoomFactor
< 3.9 );
2623 d
->aZoomOut
->setEnabled( d
->zoomFactor
> 0.2 );
2626 void PageView::updateZoomText()
2628 // use current page zoom as zoomFactor if in ZoomFit/* mode
2629 if ( d
->zoomMode
!= ZoomFixed
&& d
->items
.count() > 0 )
2630 d
->zoomFactor
= d
->items
[ qMax( 0, (int)d
->document
->currentPage() ) ]->zoomFactor();
2631 float newFactor
= d
->zoomFactor
;
2632 d
->aZoom
->removeAllActions();
2634 // add items that describe fit actions
2635 QStringList translated
;
2636 translated
<< i18n("Fit Width") << i18n("Fit Page") /*<< i18n("Fit Text")*/;
2638 // add percent items
2639 QString
double_oh( "00" );
2640 const float zoomValue
[10] = { 0.12, 0.25, 0.33, 0.50, 0.66, 0.75, 1.00, 1.25, 1.50, 2.00 };
2642 selIdx
= 2; // use 3 if "fit text" present
2643 bool inserted
= false; //use: "d->zoomMode != ZoomFixed" to hide Fit/* zoom ratio
2644 while ( idx
< 10 || !inserted
)
2646 float value
= idx
< 10 ? zoomValue
[ idx
] : newFactor
;
2647 if ( !inserted
&& newFactor
< (value
- 0.0001) )
2651 if ( value
> (newFactor
- 0.0001) && value
< (newFactor
+ 0.0001) )
2655 QString
localValue( KGlobal::locale()->formatNumber( value
* 100.0, 2 ) );
2656 localValue
.remove( KGlobal::locale()->decimalSymbol() + double_oh
);
2657 // remove a trailing zero in numbers like 66.70
2658 if ( localValue
.right( 1 ) == QLatin1String( "0" ) && localValue
.indexOf( KGlobal::locale()->decimalSymbol() ) > -1 )
2659 localValue
.chop( 1 );
2660 translated
<< QString( "%1%" ).arg( localValue
);
2662 d
->aZoom
->setItems( translated
);
2664 // select current item in list
2665 if ( d
->zoomMode
== ZoomFitWidth
)
2667 else if ( d
->zoomMode
== ZoomFitPage
)
2669 else if ( d
->zoomMode
== ZoomFitText
)
2671 d
->aZoom
->setCurrentItem( selIdx
);
2674 void PageView::updateCursor( const QPoint
&p
)
2676 // detect the underlaying page (if present)
2677 PageViewItem
* pageItem
= pickItemOnPoint( p
.x(), p
.y() );
2680 double nX
= pageItem
->absToPageX(p
.x());
2681 double nY
= pageItem
->absToPageY(p
.y());
2683 // if over a ObjectRect (of type Link) change cursor to hand
2684 if ( d
->mouseMode
== MouseTextSelect
)
2685 setCursor( Qt::IBeamCursor
);
2686 else if ( d
->mouseAnn
)
2687 setCursor( Qt::ClosedHandCursor
);
2690 const Okular::ObjectRect
* linkobj
= pageItem
->page()->objectRect( Okular::ObjectRect::Action
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
2691 const Okular::ObjectRect
* annotobj
= pageItem
->page()->objectRect( Okular::ObjectRect::OAnnotation
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
2692 if ( linkobj
&& !annotobj
)
2694 d
->mouseOnRect
= true;
2695 setCursor( Qt::PointingHandCursor
);
2699 d
->mouseOnRect
= false;
2701 && ( QApplication::keyboardModifiers() & Qt::ControlModifier
)
2702 && static_cast< const Okular::AnnotationObjectRect
* >( annotobj
)->annotation()->canBeMoved() )
2704 setCursor( Qt::OpenHandCursor
);
2708 setCursor( Qt::ArrowCursor
);
2715 // if there's no page over the cursor and we were showing the pointingHandCursor
2716 // go back to the normal one
2717 d
->mouseOnRect
= false;
2718 setCursor( Qt::ArrowCursor
);
2722 int PageView::viewColumns() const
2724 int nr
= Okular::Settings::viewMode();
2727 return Okular::Settings::viewColumns();
2730 int PageView::viewRows() const
2732 if ( Okular::Settings::viewMode() < 2 )
2734 return Okular::Settings::viewRows();
2737 void PageView::center(int cx
, int cy
)
2739 horizontalScrollBar()->setValue(cx
- viewport()->width() / 2);
2740 verticalScrollBar()->setValue(cy
- viewport()->height() / 2);
2743 void PageView::toggleFormWidgets( bool on
)
2745 bool somehadfocus
= false;
2746 QVector
< PageViewItem
* >::const_iterator dIt
= d
->items
.begin(), dEnd
= d
->items
.end();
2747 for ( ; dIt
!= dEnd
; ++dIt
)
2749 bool hadfocus
= (*dIt
)->setFormWidgetsVisible( on
);
2750 somehadfocus
= somehadfocus
|| hadfocus
;
2754 d
->m_formsVisible
= on
;
2755 if ( d
->aToggleForms
) // it may not exist if we are on dummy mode
2757 if ( d
->m_formsVisible
)
2759 d
->aToggleForms
->setText( i18n( "Hide Forms" ) );
2763 d
->aToggleForms
->setText( i18n( "Show Forms" ) );
2768 //BEGIN private SLOTS
2769 void PageView::slotRelayoutPages()
2770 // called by: notifySetup, viewportResizeEvent, slotViewMode, slotContinuousToggled, updateZoom
2772 // set an empty container if we have no pages
2773 int pageCount
= d
->items
.count();
2774 if ( pageCount
< 1 )
2776 setWidgetResizable(true);
2780 // if viewport was auto-moving, stop it
2781 if ( d
->viewportMoveActive
)
2783 center( d
->viewportMoveDest
.x(), d
->viewportMoveDest
.y() );
2784 d
->viewportMoveActive
= false;
2785 d
->viewportMoveTimer
->stop();
2786 verticalScrollBar()->setEnabled( true );
2787 horizontalScrollBar()->setEnabled( true );
2790 // common iterator used in this method and viewport parameters
2791 QVector
< PageViewItem
* >::const_iterator iIt
, iEnd
= d
->items
.end();
2792 int viewportWidth
= viewport()->width(),
2793 viewportHeight
= viewport()->height(),
2796 QRect
viewportRect( horizontalScrollBar()->value(), verticalScrollBar()->value(), viewportWidth
, viewportHeight
);
2798 // handle the 'center first page in row' stuff
2799 int nCols
= viewColumns();
2800 bool centerFirstPage
= Okular::Settings::centerFirstPageInRow() && nCols
> 1;
2801 const bool continuousView
= Okular::Settings::viewContinuous();
2803 // set all items geometry and resize contents. handle 'continuous' and 'single' modes separately
2805 PageViewItem
* currentItem
= d
->items
[ qMax( 0, (int)d
->document
->currentPage() ) ];
2807 // handle the 'centering on first row' stuff
2808 if ( centerFirstPage
)
2809 pageCount
+= nCols
- 1;
2810 // Here we find out column's width and row's height to compute a table
2811 // so we can place widgets 'centered in virtual cells'.
2814 // if ( Okular::Settings::viewMode() < 2 )
2815 nRows
= (int)ceil( (float)pageCount
/ (float)nCols
);
2816 // nRows=(int)ceil( (float)pageCount / (float) Okular::Settings::viewRows() );
2818 // nRows = Okular::Settings::viewRows();
2820 int * colWidth
= new int[ nCols
],
2821 * rowHeight
= new int[ nRows
],
2824 for ( int i
= 0; i
< nCols
; i
++ )
2825 colWidth
[ i
] = viewportWidth
/ nCols
;
2826 for ( int i
= 0; i
< nRows
; i
++ )
2828 // handle the 'centering on first row' stuff
2829 if ( centerFirstPage
)
2831 pageCount
-= nCols
- 1;
2835 // 1) find the maximum columns width and rows height for a grid in
2836 // which each page must well-fit inside a cell
2837 for ( iIt
= d
->items
.begin(); iIt
!= iEnd
; ++iIt
)
2839 PageViewItem
* item
= *iIt
;
2840 // update internal page size (leaving a little margin in case of Fit* modes)
2841 updateItemSize( item
, colWidth
[ cIdx
] - 6, viewportHeight
- 12 );
2842 // find row's maximum height and column's max width
2843 if ( item
->croppedWidth() + 6 > colWidth
[ cIdx
] )
2844 colWidth
[ cIdx
] = item
->croppedWidth() + 6;
2845 if ( item
->croppedHeight() + 12 > rowHeight
[ rIdx
] )
2846 rowHeight
[ rIdx
] = item
->croppedHeight() + 12;
2847 // handle the 'centering on first row' stuff
2848 // update col/row indices
2849 if ( ++cIdx
== nCols
)
2856 const int pageRowIdx
= ( ( centerFirstPage
? nCols
- 1 : 0 ) + currentItem
->pageNumber() ) / nCols
;
2858 // 2) compute full size
2859 for ( int i
= 0; i
< nCols
; i
++ )
2860 fullWidth
+= colWidth
[ i
];
2861 if ( continuousView
)
2863 for ( int i
= 0; i
< nRows
; i
++ )
2864 fullHeight
+= rowHeight
[ i
];
2867 fullHeight
= rowHeight
[ pageRowIdx
];
2869 if (!horizontalScrollBar()->isVisible() && !verticalScrollBar()->isVisible() && fullWidth
== viewportWidth
&&
2870 fullHeight
- viewportHeight
== 1 && d
->zoomMode
== ZoomFitWidth
)
2872 // this saves us from infinite resizing loop because of scrollbars appearing and disappearing
2874 fullHeight
= viewportHeight
;
2877 // 3) arrange widgets inside cells (and refine fullHeight if needed)
2879 insertY
= fullHeight
< viewportHeight
? ( viewportHeight
- fullHeight
) / 2 : 0;
2880 const int origInsertY
= insertY
;
2883 if ( centerFirstPage
)
2886 for ( int i
= 0; i
< cIdx
; ++i
)
2887 insertX
+= colWidth
[ i
];
2889 for ( iIt
= d
->items
.begin(); iIt
!= iEnd
; ++iIt
)
2891 PageViewItem
* item
= *iIt
;
2892 int cWidth
= colWidth
[ cIdx
],
2893 rHeight
= rowHeight
[ rIdx
];
2894 if ( continuousView
|| rIdx
== pageRowIdx
)
2896 const bool reallyDoCenterFirst
= item
->pageNumber() == 0 && centerFirstPage
;
2897 item
->moveTo( (reallyDoCenterFirst
? 0 : insertX
) + ( (reallyDoCenterFirst
? fullWidth
: cWidth
) - item
->croppedWidth()) / 2,
2898 (continuousView
? insertY
: origInsertY
) + (rHeight
- item
->croppedHeight()) / 2 );
2899 item
->setVisible( true );
2903 item
->moveTo( 0, 0 );
2904 item
->setVisible( false );
2906 item
->setFormWidgetsVisible( d
->m_formsVisible
);
2907 // advance col/row index
2909 if ( ++cIdx
== nCols
)
2916 #ifdef PAGEVIEW_DEBUG
2917 kWarning() << "updating size for pageno" << item
->pageNumber() << "cropped" << item
->croppedGeometry() << "uncropped" << item
->uncroppedGeometry();
2922 delete [] rowHeight
;
2924 // 3) reset dirty state
2925 d
->dirtyLayout
= false;
2927 horizontalScrollBar()->setRange( 0, qMax( 0, fullWidth
- viewport()->width() ) );
2928 verticalScrollBar()->setRange( 0, qMax( 0, fullHeight
- viewport()->height() ) );
2930 // 4) update scrollview's contents size and recenter view
2931 bool wasUpdatesEnabled
= viewport()->updatesEnabled();
2932 if ( fullWidth
!= widget()->width() || fullHeight
!= widget()->height() )
2934 // disable updates and resize the viewportContents
2935 if ( wasUpdatesEnabled
)
2936 viewport()->setUpdatesEnabled( false );
2937 setWidgetResizable(false);
2938 fullWidth
= qMax(fullWidth
, viewport()->width());
2939 fullHeight
= qMax(fullHeight
, viewport()->height());
2940 widget()->resize( fullWidth
, fullHeight
);
2941 // restore previous viewport if defined and updates enabled
2942 if ( wasUpdatesEnabled
)
2944 const Okular::DocumentViewport
& vp
= d
->document
->viewport();
2945 if ( vp
.pageNumber
>= 0 )
2947 int prevX
= horizontalScrollBar()->value(),
2948 prevY
= verticalScrollBar()->value();
2949 const QRect
& geometry
= d
->items
[ vp
.pageNumber
]->croppedGeometry();
2950 double nX
= vp
.rePos
.enabled
? vp
.rePos
.normalizedX
: 0.5,
2951 nY
= vp
.rePos
.enabled
? vp
.rePos
.normalizedY
: 0.0;
2952 center( geometry
.left() + qRound( nX
* (double)geometry
.width() ),
2953 geometry
.top() + qRound( nY
* (double)geometry
.height() ) );
2954 // center() usually moves the viewport, that requests pixmaps too.
2955 // if that doesn't happen we have to request them by hand
2956 if ( prevX
== horizontalScrollBar()->value() && prevY
== verticalScrollBar()->value() )
2957 slotRequestVisiblePixmaps();
2959 // or else go to center page
2961 center( fullWidth
/ 2, 0 );
2962 viewport()->setUpdatesEnabled( true );
2966 // 5) update the whole viewport if updated enabled
2967 if ( wasUpdatesEnabled
)
2971 void PageView::slotRequestVisiblePixmaps( int newValue
)
2973 // if requests are blocked (because raised by an unwanted event), exit
2974 if ( d
->blockPixmapsRequest
|| d
->viewportMoveActive
||
2975 d
->mouseMidZooming
)
2978 // precalc view limits for intersecting with page coords inside the lOOp
2979 bool isEvent
= newValue
!= -1 && !d
->blockViewport
;
2980 QRect
viewportRect( horizontalScrollBar()->value(),
2981 verticalScrollBar()->value(),
2982 viewport()->width(), viewport()->height() );
2984 // some variables used to determine the viewport
2985 int nearPageNumber
= -1;
2986 double viewportCenterX
= (viewportRect
.left() + viewportRect
.right()) / 2.0,
2987 viewportCenterY
= (viewportRect
.top() + viewportRect
.bottom()) / 2.0,
2992 // iterate over all items
2993 d
->visibleItems
.clear();
2994 QLinkedList
< Okular::PixmapRequest
* > requestedPixmaps
;
2995 QVector
< Okular::VisiblePageRect
* > visibleRects
;
2996 QVector
< PageViewItem
* >::const_iterator iIt
= d
->items
.begin(), iEnd
= d
->items
.end();
2997 for ( ; iIt
!= iEnd
; ++iIt
)
2999 PageViewItem
* i
= *iIt
;
3000 if ( !i
->isVisible() )
3002 #ifdef PAGEVIEW_DEBUG
3003 kWarning() << "checking page" << i
->pageNumber();
3004 kWarning().nospace() << "viewportRect is " << viewportRect
<< ", page item is " << i
->croppedGeometry() << " intersect : " << viewportRect
.intersects( i
->croppedGeometry() );
3006 // if the item doesn't intersect the viewport, skip it
3007 QRect intersectionRect
= viewportRect
.intersect( i
->croppedGeometry() );
3008 if ( intersectionRect
.isEmpty() )
3011 // add the item to the 'visible list'
3012 d
->visibleItems
.push_back( i
);
3013 Okular::VisiblePageRect
* vItem
= new Okular::VisiblePageRect( i
->pageNumber(), Okular::NormalizedRect( intersectionRect
.translated( -i
->uncroppedGeometry().topLeft() ), i
->uncroppedWidth(), i
->uncroppedHeight() ) );
3014 visibleRects
.push_back( vItem
);
3015 #ifdef PAGEVIEW_DEBUG
3016 kWarning() << "checking for pixmap for page" << i
->pageNumber() << "=" << i
->page()->hasPixmap( PAGEVIEW_ID
, i
->uncroppedWidth(), i
->uncroppedHeight() );
3017 kWarning() << "checking for text for page" << i
->pageNumber() << "=" << i
->page()->hasTextPage();
3019 // if the item has not the right pixmap, add a request for it
3020 // TODO: We presently request a pixmap for the full page, and then render just the crop part. This waste memory and cycles.
3021 if ( !i
->page()->hasPixmap( PAGEVIEW_ID
, i
->uncroppedWidth(), i
->uncroppedHeight() ) )
3023 #ifdef PAGEVIEW_DEBUG
3024 kWarning() << "rerequesting visible pixmaps for page" << i
->pageNumber() << "!";
3026 Okular::PixmapRequest
* p
= new Okular::PixmapRequest(
3027 PAGEVIEW_ID
, i
->pageNumber(), i
->uncroppedWidth(), i
->uncroppedHeight(), PAGEVIEW_PRIO
, true );
3028 requestedPixmaps
.push_back( p
);
3031 // look for the item closest to viewport center and the relative
3032 // position between the item and the viewport center
3035 const QRect
& geometry
= i
->croppedGeometry();
3036 // compute distance between item center and viewport center (slightly moved left)
3037 double distance
= hypot( (geometry
.left() + geometry
.right()) / 2 - (viewportCenterX
- 4),
3038 (geometry
.top() + geometry
.bottom()) / 2 - viewportCenterY
);
3039 if ( distance
>= minDistance
&& nearPageNumber
!= -1 )
3041 nearPageNumber
= i
->pageNumber();
3042 minDistance
= distance
;
3043 if ( geometry
.height() > 0 && geometry
.width() > 0 )
3045 focusedX
= ( viewportCenterX
- (double)geometry
.left() ) / (double)geometry
.width();
3046 focusedY
= ( viewportCenterY
- (double)geometry
.top() ) / (double)geometry
.height();
3051 // if preloading is enabled, add the pages before and after in preloading
3052 if ( !d
->visibleItems
.isEmpty() &&
3053 Okular::Settings::memoryLevel() != Okular::Settings::EnumMemoryLevel::Low
&&
3054 Okular::Settings::enableThreading() )
3056 // as the requests are done in the order as they appear in the list,
3057 // request first the next page and then the previous
3059 // add the page after the 'visible series' in preload
3060 int tailRequest
= d
->visibleItems
.last()->pageNumber() + 1;
3061 if ( tailRequest
< (int)d
->items
.count() )
3063 PageViewItem
* i
= d
->items
[ tailRequest
];
3064 // request the pixmap if not already present
3065 if ( !i
->page()->hasPixmap( PAGEVIEW_ID
, i
->uncroppedWidth(), i
->uncroppedHeight() ) && i
->uncroppedWidth() > 0 )
3066 requestedPixmaps
.push_back( new Okular::PixmapRequest(
3067 PAGEVIEW_ID
, i
->pageNumber(), i
->uncroppedWidth(), i
->uncroppedHeight(), PAGEVIEW_PRELOAD_PRIO
, true ) );
3070 // add the page before the 'visible series' in preload
3071 int headRequest
= d
->visibleItems
.first()->pageNumber() - 1;
3072 if ( headRequest
>= 0 )
3074 PageViewItem
* i
= d
->items
[ headRequest
];
3075 // request the pixmap if not already present
3076 if ( !i
->page()->hasPixmap( PAGEVIEW_ID
, i
->uncroppedWidth(), i
->uncroppedHeight() ) && i
->uncroppedWidth() > 0 )
3077 requestedPixmaps
.push_back( new Okular::PixmapRequest(
3078 PAGEVIEW_ID
, i
->pageNumber(), i
->uncroppedWidth(), i
->uncroppedHeight(), PAGEVIEW_PRELOAD_PRIO
, true ) );
3082 // send requests to the document
3083 if ( !requestedPixmaps
.isEmpty() )
3085 d
->document
->requestPixmaps( requestedPixmaps
);
3087 // if this functions was invoked by viewport events, send update to document
3088 if ( isEvent
&& nearPageNumber
!= -1 )
3090 // determine the document viewport
3091 Okular::DocumentViewport
newViewport( nearPageNumber
);
3092 newViewport
.rePos
.enabled
= true;
3093 newViewport
.rePos
.normalizedX
= focusedX
;
3094 newViewport
.rePos
.normalizedY
= focusedY
;
3095 // set the viewport to other observers
3096 d
->document
->setViewport( newViewport
, PAGEVIEW_ID
);
3098 d
->document
->setVisiblePageRects( visibleRects
, PAGEVIEW_ID
);
3101 void PageView::slotMoveViewport()
3103 // converge to viewportMoveDest in 1 second
3104 int diffTime
= d
->viewportMoveTime
.elapsed();
3105 if ( diffTime
>= 667 || !d
->viewportMoveActive
)
3107 center( d
->viewportMoveDest
.x(), d
->viewportMoveDest
.y() );
3108 d
->viewportMoveTimer
->stop();
3109 d
->viewportMoveActive
= false;
3110 slotRequestVisiblePixmaps();
3111 verticalScrollBar()->setEnabled( true );
3112 horizontalScrollBar()->setEnabled( true );
3116 // move the viewport smoothly (kmplot: p(x)=1+0.47*(x-1)^3-0.25*(x-1)^4)
3117 float convergeSpeed
= (float)diffTime
/ 667.0,
3118 x
= ((float)viewport()->width() / 2.0) + horizontalScrollBar()->value(),
3119 y
= ((float)viewport()->height() / 2.0) + verticalScrollBar()->value(),
3120 diffX
= (float)d
->viewportMoveDest
.x() - x
,
3121 diffY
= (float)d
->viewportMoveDest
.y() - y
;
3122 convergeSpeed
*= convergeSpeed
* (1.4 - convergeSpeed
);
3123 center( (int)(x
+ diffX
* convergeSpeed
),
3124 (int)(y
+ diffY
* convergeSpeed
) );
3127 void PageView::slotAutoScoll()
3129 // the first time create the timer
3130 if ( !d
->autoScrollTimer
)
3132 d
->autoScrollTimer
= new QTimer( this );
3133 d
->autoScrollTimer
->setSingleShot( true );
3134 connect( d
->autoScrollTimer
, SIGNAL( timeout() ), this, SLOT( slotAutoScoll() ) );
3137 // if scrollIncrement is zero, stop the timer
3138 if ( !d
->scrollIncrement
)
3140 d
->autoScrollTimer
->stop();
3144 // compute delay between timer ticks and scroll amount per tick
3145 int index
= abs( d
->scrollIncrement
) - 1; // 0..9
3146 const int scrollDelay
[10] = { 200, 100, 50, 30, 20, 30, 25, 20, 30, 20 };
3147 const int scrollOffset
[10] = { 1, 1, 1, 1, 1, 2, 2, 2, 4, 4 };
3148 d
->autoScrollTimer
->start( scrollDelay
[ index
] );
3149 int delta
= d
->scrollIncrement
> 0 ? scrollOffset
[ index
] : -scrollOffset
[ index
];
3150 verticalScrollBar()->setValue(verticalScrollBar()->value() + delta
);
3153 void PageView::slotDragScroll()
3155 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + d
->dragScrollVector
.x());
3156 verticalScrollBar()->setValue(verticalScrollBar()->value() + d
->dragScrollVector
.y());
3157 QPoint p
= widget()->mapFromGlobal( QCursor::pos() );
3158 selectionEndPoint( p
);
3161 void PageView::slotShowWelcome()
3163 // show initial welcome text
3164 d
->messageWindow
->display( i18n( "Welcome" ), PageViewMessage::Info
, 2000 );
3167 void PageView::slotZoom()
3170 updateZoom( ZoomFixed
);
3173 void PageView::slotZoomIn()
3175 updateZoom( ZoomIn
);
3178 void PageView::slotZoomOut()
3180 updateZoom( ZoomOut
);
3183 void PageView::slotFitToWidthToggled( bool on
)
3185 if ( on
) updateZoom( ZoomFitWidth
);
3188 void PageView::slotFitToPageToggled( bool on
)
3190 if ( on
) updateZoom( ZoomFitPage
);
3193 void PageView::slotFitToTextToggled( bool on
)
3195 if ( on
) updateZoom( ZoomFitText
);
3198 void PageView::slotViewMode( int nr
)
3200 if ( (int)Okular::Settings::viewMode() != nr
)
3202 Okular::Settings::setViewMode( nr
);
3203 Okular::Settings::self()->writeConfig();
3204 if ( d
->document
->pages() > 0 )
3205 slotRelayoutPages();
3209 void PageView::slotContinuousToggled( bool on
)
3211 if ( Okular::Settings::viewContinuous() != on
)
3213 Okular::Settings::setViewContinuous( on
);
3214 Okular::Settings::self()->writeConfig();
3215 if ( d
->document
->pages() > 0 )
3216 slotRelayoutPages();
3220 void PageView::slotSetMouseNormal()
3222 d
->mouseMode
= MouseNormal
;
3223 // hide the messageWindow
3224 d
->messageWindow
->hide();
3225 // reshow the annotator toolbar if hiding was forced
3226 if ( d
->aToggleAnnotator
->isChecked() )
3227 slotToggleAnnotator( true );
3228 // force an update of the cursor
3229 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
3232 void PageView::slotSetMouseZoom()
3234 d
->mouseMode
= MouseZoom
;
3235 // change the text in messageWindow (and show it if hidden)
3236 d
->messageWindow
->display( i18n( "Select zooming area. Right-click to zoom out." ), PageViewMessage::Info
, -1 );
3237 // force hiding of annotator toolbar
3239 d
->annotator
->setEnabled( false );
3240 // force an update of the cursor
3241 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
3244 void PageView::slotSetMouseSelect()
3246 d
->mouseMode
= MouseSelect
;
3247 // change the text in messageWindow (and show it if hidden)
3248 d
->messageWindow
->display( i18n( "Draw a rectangle around the text/graphics to copy." ), PageViewMessage::Info
, -1 );
3249 // force hiding of annotator toolbar
3251 d
->annotator
->setEnabled( false );
3252 // force an update of the cursor
3253 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
3256 void PageView::slotSetMouseTextSelect()
3258 d
->mouseMode
= MouseTextSelect
;
3259 // change the text in messageWindow (and show it if hidden)
3260 d
->messageWindow
->display( i18n( "Select text" ), PageViewMessage::Info
, -1 );
3261 // force hiding of annotator toolbar
3263 d
->annotator
->setEnabled( false );
3264 // force an update of the cursor
3265 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
3268 void PageView::slotToggleAnnotator( bool on
)
3270 // the 'inHere' trick is needed as the slotSetMouseZoom() calls this
3271 static bool inHere
= false;
3276 // the annotator can be used in normal mouse mode only, so if asked for it,
3277 // switch to normal mode
3278 if ( on
&& d
->mouseMode
!= MouseNormal
)
3279 d
->aMouseNormal
->trigger();
3281 // create the annotator object if not present
3282 if ( !d
->annotator
)
3284 d
->annotator
= new PageViewAnnotator( this, d
->document
);
3285 bool allowTools
= d
->document
->pages() > 0 && d
->document
->isAllowed( Okular::AllowNotes
);
3286 d
->annotator
->setToolsEnabled( allowTools
);
3287 d
->annotator
->setTextToolsEnabled( allowTools
&& d
->document
->supportsSearching() );
3290 // initialize/reset annotator (and show/hide toolbar)
3291 d
->annotator
->setEnabled( on
);
3296 void PageView::slotScrollUp()
3298 if ( d
->scrollIncrement
< -9 )
3300 d
->scrollIncrement
--;
3305 void PageView::slotScrollDown()
3307 if ( d
->scrollIncrement
> 9 )
3309 d
->scrollIncrement
++;
3314 void PageView::slotRotateClockwise()
3316 int id
= ( (int)d
->document
->rotation() + 1 ) % 4;
3317 d
->document
->setRotation( id
);
3320 void PageView::slotRotateCounterClockwise()
3322 int id
= ( (int)d
->document
->rotation() + 3 ) % 4;
3323 d
->document
->setRotation( id
);
3326 void PageView::slotRotateOriginal()
3328 d
->document
->setRotation( 0 );
3331 void PageView::slotPageSizes( int newsize
)
3333 if ( newsize
< 0 || newsize
>= d
->document
->pageSizes().count() )
3336 d
->document
->setPageSize( d
->document
->pageSizes().at( newsize
) );
3339 void PageView::slotTrimMarginsToggled( bool on
)
3341 if ( Okular::Settings::trimMargins() != on
)
3343 Okular::Settings::setTrimMargins( on
);
3344 Okular::Settings::self()->writeConfig();
3345 if ( d
->document
->pages() > 0 )
3347 slotRelayoutPages();
3348 slotRequestVisiblePixmaps(); // TODO: slotRelayoutPages() may have done this already!
3353 void PageView::slotToggleForms()
3355 toggleFormWidgets( !d
->m_formsVisible
);
3358 void PageView::slotFormWidgetChanged( FormWidgetIface
*w
)
3360 if ( !d
->refreshTimer
)
3362 d
->refreshTimer
= new QTimer( this );
3363 d
->refreshTimer
->setSingleShot( true );
3364 connect( d
->refreshTimer
, SIGNAL( timeout() ),
3365 this, SLOT( slotRefreshPage() ) );
3367 d
->refreshPage
= w
->pageItem()->pageNumber();
3368 d
->refreshTimer
->start( 1000 );
3371 void PageView::slotRefreshPage()
3373 const int req
= d
->refreshPage
;
3376 d
->refreshPage
= -1;
3377 QMetaObject::invokeMethod( d
->document
, "refreshPixmaps", Qt::QueuedConnection
,
3378 Q_ARG( int, req
) );
3381 void PageView::slotSpeakDocument()
3384 QVector
< PageViewItem
* >::const_iterator it
= d
->items
.begin(), itEnd
= d
->items
.end();
3385 for ( ; it
< itEnd
; ++it
)
3387 Okular::RegularAreaRect
* area
= textSelectionForItem( *it
);
3388 text
.append( (*it
)->page()->text( area
) );
3389 text
.append( '\n' );
3393 d
->tts()->say( text
);
3396 void PageView::slotSpeakCurrentPage()
3398 const int currentPage
= d
->document
->viewport().pageNumber
;
3400 PageViewItem
*item
= d
->items
.at( currentPage
);
3401 Okular::RegularAreaRect
* area
= textSelectionForItem( item
);
3402 const QString text
= item
->page()->text( area
);
3405 d
->tts()->say( text
);
3408 void PageView::slotStopSpeaks()
3413 d
->m_tts
->stopAllSpeechs();
3416 void PageView::slotAction( Okular::Action
*action
)
3418 d
->document
->processAction( action
);
3422 #include "pageview.moc"
3424 /* kate: replace-tabs on; indent-width 4; */