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 <kactionmenu.h>
35 #include <kstandardaction.h>
36 #include <kactioncollection.h>
39 #include <kfiledialog.h>
41 #include <kglobalsettings.h>
42 #include <kselectaction.h>
43 #include <ktoggleaction.h>
45 #include <kmessagebox.h>
53 #include "formwidgets.h"
54 #include "pageviewutils.h"
55 #include "pagepainter.h"
56 #include "core/annotations.h"
57 #include "annotwindow.h"
59 #include "annotationpopup.h"
60 #include "pageviewannotator.h"
61 #include "toolaction.h"
63 #include "videowidget.h"
64 #include "core/action.h"
65 #include "core/document.h"
66 #include "core/form.h"
67 #include "core/page.h"
68 #include "core/misc.h"
69 #include "core/generator.h"
70 #include "core/movie.h"
73 static int pageflags
= PagePainter::Accessibility
| PagePainter::EnhanceLinks
|
74 PagePainter::EnhanceImages
| PagePainter::Highlights
|
75 PagePainter::TextSelection
| PagePainter::Annotations
;
77 // structure used internally by PageView for data storage
81 PageViewPrivate( PageView
*qq
);
83 FormWidgetsController
* formWidgetsController();
85 QString
selectedText() const;
87 // the document, pageviewItems and the 'visible cache'
89 Okular::Document
* document
;
90 QVector
< PageViewItem
* > items
;
91 QLinkedList
< PageViewItem
* > visibleItems
;
93 // view layout (columns and continuous in Settings), zoom and mouse
94 PageView::ZoomMode zoomMode
;
96 PageView::MouseMode mouseMode
;
99 QPoint mouseSelectPos
;
100 bool mouseMidZooming
;
103 QRect mouseSelectionRect
;
104 QColor mouseSelectionColor
;
105 bool mouseTextSelecting
;
106 QSet
< int > pagesWithTextSelection
;
108 Okular::Annotation
* mouseAnn
;
112 bool viewportMoveActive
;
113 QTime viewportMoveTime
;
114 QPoint viewportMoveDest
;
115 QTimer
* viewportMoveTimer
;
118 QTimer
* autoScrollTimer
;
120 PageViewAnnotator
* annotator
;
121 //text annotation dialogs list
122 QHash
< Okular::Annotation
*, AnnotWindow
* > m_annowindows
;
124 QTimer
* delayResizeTimer
;
126 bool blockViewport
; // prevents changes to viewport
127 bool blockPixmapsRequest
; // prevent pixmap requests
128 PageViewMessage
* messageWindow
; // in pageviewutils.h
130 FormWidgetsController
*formsWidgetController
;
132 QTimer
* refreshTimer
;
135 // infinite resizing loop prevention
136 bool bothScrollbarsVisible
;
139 QPoint dragScrollVector
;
140 QTimer dragScrollTimer
;
143 KAction
* aRotateClockwise
;
144 KAction
* aRotateCounterClockwise
;
145 KAction
* aRotateOriginal
;
146 KSelectAction
* aPageSizes
;
147 KToggleAction
* aTrimMargins
;
148 KAction
* aMouseNormal
;
149 KAction
* aMouseSelect
;
150 KAction
* aMouseTextSelect
;
151 KToggleAction
* aToggleAnnotator
;
152 KSelectAction
* aZoom
;
155 KToggleAction
* aZoomFitWidth
;
156 KToggleAction
* aZoomFitPage
;
157 KToggleAction
* aZoomFitText
;
158 KActionMenu
* aViewMode
;
159 KToggleAction
* aViewContinuous
;
160 QAction
* aPrevAction
;
161 KAction
* aToggleForms
;
163 KAction
* aSpeakPage
;
164 KAction
* aSpeakStop
;
165 KActionCollection
* actionCollection
;
167 int setting_viewMode
;
168 int setting_viewCols
;
169 bool setting_centerFirst
;
172 PageViewPrivate::PageViewPrivate( PageView
*qq
)
177 FormWidgetsController
* PageViewPrivate::formWidgetsController()
179 if ( !formsWidgetController
)
181 formsWidgetController
= new FormWidgetsController();
182 QObject::connect( formsWidgetController
, SIGNAL( changed( FormWidgetIface
* ) ),
183 q
, SLOT( slotFormWidgetChanged( FormWidgetIface
* ) ) );
184 QObject::connect( formsWidgetController
, SIGNAL( action( Okular::Action
* ) ),
185 q
, SLOT( slotAction( Okular::Action
* ) ) );
188 return formsWidgetController
;
191 OkularTTS
* PageViewPrivate::tts()
195 m_tts
= new OkularTTS( q
);
198 QObject::connect( m_tts
, SIGNAL( hasSpeechs( bool ) ),
199 aSpeakStop
, SLOT( setEnabled( bool ) ) );
200 QObject::connect( m_tts
, SIGNAL( errorMessage( const QString
& ) ),
201 q
, SLOT( errorMessage( const QString
& ) ) );
209 class PageViewWidget
: public QWidget
212 PageViewWidget(PageView
*pv
) : QWidget(pv
), m_pageView(pv
) {}
215 bool event( QEvent
*e
)
217 if ( e
->type() == QEvent::ToolTip
&& m_pageView
->d
->mouseMode
== PageView::MouseNormal
)
219 QHelpEvent
* he
= (QHelpEvent
*)e
;
220 PageViewItem
* pageItem
= m_pageView
->pickItemOnPoint( he
->x(), he
->y() );
221 const Okular::ObjectRect
* rect
= 0;
222 const Okular::Action
* link
= 0;
223 const Okular::Annotation
* ann
= 0;
226 double nX
= pageItem
->absToPageX( he
->x() );
227 double nY
= pageItem
->absToPageY( he
->y() );
228 rect
= pageItem
->page()->objectRect( Okular::ObjectRect::OAnnotation
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
230 ann
= static_cast< const Okular::AnnotationObjectRect
* >( rect
)->annotation();
233 rect
= pageItem
->page()->objectRect( Okular::ObjectRect::Action
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
235 link
= static_cast< const Okular::Action
* >( rect
->object() );
241 QRect r
= rect
->boundingRect( pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
242 r
.translate( pageItem
->uncroppedGeometry().topLeft() );
243 QString tip
= GuiUtils::prettyToolTip( ann
);
244 QToolTip::showText( he
->globalPos(), tip
, this, r
);
248 QRect r
= rect
->boundingRect( pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
249 r
.translate( pageItem
->uncroppedGeometry().topLeft() );
250 QString tip
= link
->actionTip();
251 if ( !tip
.isEmpty() )
252 QToolTip::showText( he
->globalPos(), tip
, this, r
);
258 // do not stop the event
259 return QWidget::event( e
);
263 void paintEvent( QPaintEvent
*e
)
265 m_pageView
->contentsPaintEvent(e
);
268 void mouseMoveEvent( QMouseEvent
*e
)
270 m_pageView
->contentsMouseMoveEvent(e
);
273 void mousePressEvent( QMouseEvent
*e
)
275 m_pageView
->contentsMousePressEvent(e
);
278 void mouseReleaseEvent( QMouseEvent
*e
)
280 m_pageView
->contentsMouseReleaseEvent(e
);
285 PageView
*m_pageView
;
288 /* PageView. What's in this file? -> quick overview.
289 * Code weight (in rows) and meaning:
290 * 160 - constructor and creating actions plus their connected slots (empty stuff)
291 * 70 - DocumentObserver inherited methodes (important)
292 * 550 - events: mouse, keyboard, drag/drop
293 * 170 - slotRelayoutPages: set contents of the scrollview on continuous/single modes
294 * 100 - zoom: zooming pages in different ways, keeping update the toolbar actions, etc..
295 * other misc functions: only slotRequestVisiblePixmaps and pickItemOnPoint noticeable,
296 * and many insignificant stuff like this comment :-)
298 PageView::PageView( QWidget
*parent
, Okular::Document
*document
)
299 : QScrollArea( parent
)
300 , Okular::View( QString::fromLatin1( "PageView" ) )
302 // create and initialize private storage structure
303 d
= new PageViewPrivate( this );
304 d
->document
= document
;
305 d
->aRotateClockwise
= 0;
306 d
->aRotateCounterClockwise
= 0;
307 d
->aRotateOriginal
= 0;
309 d
->zoomMode
= PageView::ZoomFitWidth
;
311 d
->mouseMode
= MouseNormal
;
312 d
->mouseMidZooming
= false;
313 d
->mouseSelecting
= false;
314 d
->mouseTextSelecting
= false;
315 d
->mouseOnRect
= false;
317 d
->viewportMoveActive
= false;
318 d
->viewportMoveTimer
= 0;
319 d
->scrollIncrement
= 0;
320 d
->autoScrollTimer
= 0;
322 d
->delayResizeTimer
= 0;
323 d
->dirtyLayout
= false;
324 d
->blockViewport
= false;
325 d
->blockPixmapsRequest
= false;
326 d
->messageWindow
= new PageViewMessage(this);
327 d
->m_formsVisible
= false;
328 d
->formsWidgetController
= 0;
332 d
->aRotateClockwise
= 0;
333 d
->aRotateCounterClockwise
= 0;
334 d
->aRotateOriginal
= 0;
339 d
->aMouseTextSelect
= 0;
340 d
->aToggleAnnotator
= 0;
341 d
->aZoomFitWidth
= 0;
345 d
->aViewContinuous
= 0;
351 d
->actionCollection
= 0;
353 d
->setting_viewMode
= Okular::Settings::viewMode();
354 d
->setting_viewCols
= Okular::Settings::viewColumns();
355 d
->setting_centerFirst
= Okular::Settings::centerFirstPageInRow();
357 setFrameStyle(QFrame::NoFrame
);
359 setAttribute( Qt::WA_StaticContents
);
361 setObjectName( QLatin1String( "okular::pageView" ) );
363 // widget setup: setup focus, accept drops and track mouse
364 setWidget(new PageViewWidget(this));
365 viewport()->setFocusProxy( this );
366 viewport()->setFocusPolicy( Qt::StrongFocus
);
367 widget()->setAttribute( Qt::WA_OpaquePaintEvent
);
368 widget()->setAttribute( Qt::WA_NoSystemBackground
);
369 setAcceptDrops( true );
370 widget()->setMouseTracking( true );
371 setWidgetResizable(true);
373 // conntect the padding of the viewport to pixmaps requests
374 connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotRequestVisiblePixmaps(int)));
375 connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotRequestVisiblePixmaps(int)));
376 connect( &d
->dragScrollTimer
, SIGNAL(timeout()), this, SLOT(slotDragScroll()) );
378 // set a corner button to resize the view to the page size
379 // QPushButton * resizeButton = new QPushButton( viewport() );
380 // resizeButton->setPixmap( SmallIcon("crop") );
381 // setCornerWidget( resizeButton );
382 // resizeButton->setEnabled( false );
384 setAttribute( Qt::WA_InputMethodEnabled
, true );
386 // schedule the welcome message
387 QMetaObject::invokeMethod(this, "slotShowWelcome", Qt::QueuedConnection
);
390 PageView::~PageView()
393 d
->m_tts
->stopAllSpeechs();
395 // delete the local storage structure
396 qDeleteAll(d
->m_annowindows
);
397 // delete all widgets
398 QVector
< PageViewItem
* >::const_iterator dIt
= d
->items
.constBegin(), dEnd
= d
->items
.constEnd();
399 for ( ; dIt
!= dEnd
; ++dIt
)
401 delete d
->formsWidgetController
;
402 d
->document
->removeObserver( this );
406 void PageView::setupBaseActions( KActionCollection
* ac
)
408 d
->actionCollection
= ac
;
410 // Zoom actions ( higher scales takes lots of memory! )
411 d
->aZoom
= new KSelectAction(KIcon( "zoom-original" ), i18n("Zoom"), this);
412 ac
->addAction("zoom_to", d
->aZoom
);
413 d
->aZoom
->setEditable( true );
414 d
->aZoom
->setMaxComboViewCount( 13 );
415 connect( d
->aZoom
, SIGNAL( triggered(QAction
*) ), this, SLOT( slotZoom() ) );
418 d
->aZoomIn
= KStandardAction::zoomIn( this, SLOT( slotZoomIn() ), ac
);
420 d
->aZoomOut
= KStandardAction::zoomOut( this, SLOT( slotZoomOut() ), ac
);
423 void PageView::setupActions( KActionCollection
* ac
)
425 d
->actionCollection
= ac
;
427 // orientation menu actions
428 d
->aRotateClockwise
= new KAction( KIcon( "object-rotate-right" ), i18n( "Rotate Right" ), this );
429 d
->aRotateClockwise
->setIconText( i18nc( "Rotate right", "Right" ) );
430 ac
->addAction( "view_orientation_rotate_cw", d
->aRotateClockwise
);
431 d
->aRotateClockwise
->setEnabled( false );
432 connect( d
->aRotateClockwise
, SIGNAL( triggered() ), this, SLOT( slotRotateClockwise() ) );
433 d
->aRotateCounterClockwise
= new KAction( KIcon( "object-rotate-left" ), i18n( "Rotate Left" ), this );
434 d
->aRotateCounterClockwise
->setIconText( i18nc( "Rotate left", "Left" ) );
435 ac
->addAction( "view_orientation_rotate_ccw", d
->aRotateCounterClockwise
);
436 d
->aRotateCounterClockwise
->setEnabled( false );
437 connect( d
->aRotateCounterClockwise
, SIGNAL( triggered() ), this, SLOT( slotRotateCounterClockwise() ) );
438 d
->aRotateOriginal
= new KAction( i18n( "Original Orientation" ), this );
439 ac
->addAction( "view_orientation_original", d
->aRotateOriginal
);
440 d
->aRotateOriginal
->setEnabled( false );
441 connect( d
->aRotateOriginal
, SIGNAL( triggered() ), this, SLOT( slotRotateOriginal() ) );
443 d
->aPageSizes
= new KSelectAction(i18n("&Page Size"), this);
444 ac
->addAction("view_pagesizes", d
->aPageSizes
);
445 d
->aPageSizes
->setEnabled( false );
447 connect( d
->aPageSizes
, SIGNAL( triggered( int ) ),
448 this, SLOT( slotPageSizes( int ) ) );
450 d
->aTrimMargins
= new KToggleAction( i18n( "&Trim Margins" ), this );
451 ac
->addAction("view_trim_margins", d
->aTrimMargins
);
452 connect( d
->aTrimMargins
, SIGNAL( toggled( bool ) ), SLOT( slotTrimMarginsToggled( bool ) ) );
453 d
->aTrimMargins
->setChecked( Okular::Settings::trimMargins() );
455 d
->aZoomFitWidth
= new KToggleAction(KIcon( "zoom-fit-width" ), i18n("Fit &Width"), this);
456 ac
->addAction("view_fit_to_width", d
->aZoomFitWidth
);
457 connect( d
->aZoomFitWidth
, SIGNAL( toggled( bool ) ), SLOT( slotFitToWidthToggled( bool ) ) );
459 d
->aZoomFitPage
= new KToggleAction(KIcon( "zoom-fit-best" ), i18n("Fit &Page"), this);
460 ac
->addAction("view_fit_to_page", d
->aZoomFitPage
);
461 connect( d
->aZoomFitPage
, SIGNAL( toggled( bool ) ), SLOT( slotFitToPageToggled( bool ) ) );
464 d->aZoomFitText = new KToggleAction(KIcon( "zoom-fit-best" ), i18n("Fit &Text"), this);
465 ac->addAction("zoom_fit_text", d->aZoomFitText );
466 connect( d->aZoomFitText, SIGNAL( toggled( bool ) ), SLOT( slotFitToTextToggled( bool ) ) );
469 // View-Layout actions
470 d
->aViewMode
= new KActionMenu( KIcon( "view-split-left-right" ), i18n( "&View Mode" ), this );
471 d
->aViewMode
->setDelayed( false );
472 #define ADD_VIEWMODE_ACTION( text, name, id ) \
474 KAction *vm = new KAction( text, d->aViewMode->menu() ); \
475 vm->setCheckable( true ); \
476 vm->setData( qVariantFromValue( id ) ); \
477 d->aViewMode->addAction( vm ); \
478 ac->addAction( name, vm ); \
479 vmGroup->addAction( vm ); \
481 ac
->addAction("view_render_mode", d
->aViewMode
);
482 QActionGroup
*vmGroup
= new QActionGroup( d
->aViewMode
->menu() );
483 ADD_VIEWMODE_ACTION( i18n( "Single Page" ), "view_render_mode_single", 0 );
484 ADD_VIEWMODE_ACTION( i18n( "Facing Pages" ), "view_render_mode_facing", 1 );
485 ADD_VIEWMODE_ACTION( i18n( "Overview" ), "view_render_mode_overview", 2 );
486 d
->aViewMode
->menu()->actions().at( Okular::Settings::viewMode() )->setChecked( true );
487 connect( vmGroup
, SIGNAL( triggered( QAction
* ) ), this, SLOT( slotViewMode( QAction
* ) ) );
488 #undef ADD_VIEWMODE_ACTION
490 d
->aViewContinuous
= new KToggleAction(KIcon( "view-list-text" ), i18n("&Continuous"), this);
491 ac
->addAction("view_continuous", d
->aViewContinuous
);
492 connect( d
->aViewContinuous
, SIGNAL( toggled( bool ) ), SLOT( slotContinuousToggled( bool ) ) );
493 d
->aViewContinuous
->setChecked( Okular::Settings::viewContinuous() );
495 // Mouse-Mode actions
496 QActionGroup
* actGroup
= new QActionGroup( this );
497 actGroup
->setExclusive( true );
498 d
->aMouseNormal
= new KAction( KIcon( "input-mouse" ), i18n( "&Browse Tool" ), this );
499 ac
->addAction("mouse_drag", d
->aMouseNormal
);
500 connect( d
->aMouseNormal
, SIGNAL( triggered() ), this, SLOT( slotSetMouseNormal() ) );
501 d
->aMouseNormal
->setIconText( i18nc( "Browse Tool", "Browse" ) );
502 d
->aMouseNormal
->setCheckable( true );
503 d
->aMouseNormal
->setShortcut( Qt::CTRL
+ Qt::Key_1
);
504 d
->aMouseNormal
->setActionGroup( actGroup
);
505 d
->aMouseNormal
->setChecked( true );
507 KAction
* mz
= new KAction(KIcon( "zoom-original" ), i18n("&Zoom Tool"), this);
508 ac
->addAction("mouse_zoom", mz
);
509 connect( mz
, SIGNAL( triggered() ), this, SLOT( slotSetMouseZoom() ) );
510 mz
->setIconText( i18nc( "Zoom Tool", "Zoom" ) );
511 mz
->setCheckable( true );
512 mz
->setShortcut( Qt::CTRL
+ Qt::Key_2
);
513 mz
->setActionGroup( actGroup
);
515 d
->aMouseSelect
= new KAction(KIcon( "select-rectangular" ), i18n("&Selection Tool"), this);
516 ac
->addAction("mouse_select", d
->aMouseSelect
);
517 connect( d
->aMouseSelect
, SIGNAL( triggered() ), this, SLOT( slotSetMouseSelect() ) );
518 d
->aMouseSelect
->setIconText( i18nc( "Select Tool", "Selection" ) );
519 d
->aMouseSelect
->setCheckable( true );
520 d
->aMouseSelect
->setShortcut( Qt::CTRL
+ Qt::Key_3
);
521 d
->aMouseSelect
->setActionGroup( actGroup
);
523 d
->aMouseTextSelect
= new KAction(KIcon( "draw-text" ), i18n("&Text Selection Tool"), this);
524 ac
->addAction("mouse_textselect", d
->aMouseTextSelect
);
525 connect( d
->aMouseTextSelect
, SIGNAL( triggered() ), this, SLOT( slotSetMouseTextSelect() ) );
526 d
->aMouseTextSelect
->setIconText( i18nc( "Text Selection Tool", "Text Selection" ) );
527 d
->aMouseTextSelect
->setCheckable( true );
528 d
->aMouseTextSelect
->setShortcut( Qt::CTRL
+ Qt::Key_4
);
529 d
->aMouseTextSelect
->setActionGroup( actGroup
);
531 d
->aToggleAnnotator
= new KToggleAction(KIcon( "draw-freehand" ), i18n("&Review"), this);
532 ac
->addAction("mouse_toggle_annotate", d
->aToggleAnnotator
);
533 d
->aToggleAnnotator
->setCheckable( true );
534 connect( d
->aToggleAnnotator
, SIGNAL( toggled( bool ) ), SLOT( slotToggleAnnotator( bool ) ) );
535 d
->aToggleAnnotator
->setShortcut( Qt::Key_F6
);
537 ToolAction
*ta
= new ToolAction( this );
538 ac
->addAction( "mouse_selecttools", ta
);
539 ta
->addAction( d
->aMouseSelect
);
540 ta
->addAction( d
->aMouseTextSelect
);
543 d
->aSpeakDoc
= new KAction( KIcon( "text-speak" ), i18n( "Speak Whole Document" ), this );
544 ac
->addAction( "speak_document", d
->aSpeakDoc
);
545 d
->aSpeakDoc
->setEnabled( false );
546 connect( d
->aSpeakDoc
, SIGNAL( triggered() ), SLOT( slotSpeakDocument() ) );
548 d
->aSpeakPage
= new KAction( KIcon( "text-speak" ), i18n( "Speak Current Page" ), this );
549 ac
->addAction( "speak_current_page", d
->aSpeakPage
);
550 d
->aSpeakPage
->setEnabled( false );
551 connect( d
->aSpeakPage
, SIGNAL( triggered() ), SLOT( slotSpeakCurrentPage() ) );
553 d
->aSpeakStop
= new KAction( KIcon( "media-playback-stop" ), i18n( "Stop Speaking" ), this );
554 ac
->addAction( "speak_stop_all", d
->aSpeakStop
);
555 d
->aSpeakStop
->setEnabled( false );
556 connect( d
->aSpeakStop
, SIGNAL( triggered() ), SLOT( slotStopSpeaks() ) );
559 KAction
* su
= new KAction(i18n("Scroll Up"), this);
560 ac
->addAction("view_scroll_up", su
);
561 connect( su
, SIGNAL( triggered() ), this, SLOT( slotScrollUp() ) );
562 su
->setShortcut( QKeySequence(Qt::SHIFT
+ Qt::Key_Up
) );
565 KAction
* sd
= new KAction(i18n("Scroll Down"), this);
566 ac
->addAction("view_scroll_down", sd
);
567 connect( sd
, SIGNAL( triggered() ), this, SLOT( slotScrollDown() ) );
568 sd
->setShortcut( QKeySequence(Qt::SHIFT
+ Qt::Key_Down
) );
571 d
->aToggleForms
= new KAction( this );
572 ac
->addAction( "view_toggle_forms", d
->aToggleForms
);
573 connect( d
->aToggleForms
, SIGNAL( triggered() ), this, SLOT( slotToggleForms() ) );
574 d
->aToggleForms
->setEnabled( false );
575 toggleFormWidgets( false );
578 bool PageView::canFitPageWidth() const
580 return Okular::Settings::viewMode() != 0 || d
->zoomMode
!= ZoomFitWidth
;
583 void PageView::fitPageWidth( int page
)
585 // zoom: Fit Width, columns: 1. setActions + relayout + setPage + update
586 d
->zoomMode
= ZoomFitWidth
;
587 Okular::Settings::setViewMode( 0 );
588 d
->aZoomFitWidth
->setChecked( true );
589 d
->aZoomFitPage
->setChecked( false );
590 // d->aZoomFitText->setChecked( false );
591 d
->aViewMode
->menu()->actions().at( 0 )->setChecked( true );
592 viewport()->setUpdatesEnabled( false );
594 viewport()->setUpdatesEnabled( true );
595 d
->document
->setViewportPage( page
);
600 void PageView::setAnnotationWindow( Okular::Annotation
* annotation
)
605 // find the annot window
606 AnnotWindow
* existWindow
= 0;
607 QHash
< Okular::Annotation
*, AnnotWindow
* >::ConstIterator it
= d
->m_annowindows
.constFind( annotation
);
608 if ( it
!= d
->m_annowindows
.constEnd() )
613 if ( existWindow
== 0 )
615 existWindow
= new AnnotWindow( this, annotation
);
617 d
->m_annowindows
.insert( annotation
, existWindow
);
623 void PageView::removeAnnotationWindow( Okular::Annotation
*annotation
)
625 QHash
< Okular::Annotation
*, AnnotWindow
* >::Iterator it
= d
->m_annowindows
.find( annotation
);
626 if ( it
!= d
->m_annowindows
.end() )
629 d
->m_annowindows
.erase( it
);
633 void PageView::displayMessage( const QString
& message
,PageViewMessage::Icon icon
,int duration
)
635 if ( !Okular::Settings::showOSD() )
637 if (icon
== PageViewMessage::Error
)
638 KMessageBox::error( this, message
);
643 // hide messageWindow if string is empty
644 if ( message
.isEmpty() )
645 return d
->messageWindow
->hide();
647 // display message (duration is length dependant)
649 duration
= 500 + 100 * message
.length();
650 d
->messageWindow
->display( message
, icon
, duration
);
653 void PageView::reparseConfig()
655 // set the scroll bars policies
656 Qt::ScrollBarPolicy scrollBarMode
= Okular::Settings::showScrollBars() ?
657 Qt::ScrollBarAsNeeded
: Qt::ScrollBarAlwaysOff
;
658 if ( horizontalScrollBarPolicy() != scrollBarMode
)
660 setHorizontalScrollBarPolicy( scrollBarMode
);
661 setVerticalScrollBarPolicy( scrollBarMode
);
664 const int viewMode
= Okular::Settings::viewMode();
665 if ( ( viewMode
== 2 && ( (int)Okular::Settings::viewColumns() != d
->setting_viewCols
) )
666 || ( viewMode
> 0 && ( Okular::Settings::centerFirstPageInRow() != d
->setting_centerFirst
) )
669 d
->setting_viewMode
= Okular::Settings::viewMode();
670 d
->setting_viewCols
= Okular::Settings::viewColumns();
671 d
->setting_centerFirst
= Okular::Settings::centerFirstPageInRow();
677 KAction
*PageView::toggleFormsAction() const
679 return d
->aToggleForms
;
682 QString
PageViewPrivate::selectedText() const
684 if ( pagesWithTextSelection
.isEmpty() )
688 QList
< int > selpages
= pagesWithTextSelection
.toList();
690 const Okular::Page
* pg
= 0;
691 if ( selpages
.count() == 1 )
693 pg
= document
->page( selpages
.first() );
694 text
.append( pg
->text( pg
->textSelection() ) );
698 pg
= document
->page( selpages
.first() );
699 text
.append( pg
->text( pg
->textSelection() ) );
700 int end
= selpages
.count() - 1;
701 for( int i
= 1; i
< end
; ++i
)
703 pg
= document
->page( selpages
.at( i
) );
704 text
.append( pg
->text() );
706 pg
= document
->page( selpages
.last() );
707 text
.append( pg
->text( pg
->textSelection() ) );
712 void PageView::copyTextSelection() const
714 const QString text
= d
->selectedText();
715 if ( !text
.isEmpty() )
717 QClipboard
*cb
= QApplication::clipboard();
718 cb
->setText( text
, QClipboard::Clipboard
);
722 void PageView::selectAll()
724 if ( d
->mouseMode
!= MouseTextSelect
)
727 QVector
< PageViewItem
* >::const_iterator it
= d
->items
.constBegin(), itEnd
= d
->items
.constEnd();
728 for ( ; it
< itEnd
; ++it
)
730 Okular::RegularAreaRect
* area
= textSelectionForItem( *it
);
731 d
->pagesWithTextSelection
.insert( (*it
)->pageNumber() );
732 d
->document
->setPageTextSelection( (*it
)->pageNumber(), area
, palette().color( QPalette::Active
, QPalette::Highlight
) );
736 //BEGIN DocumentObserver inherited methods
737 void PageView::notifySetup( const QVector
< Okular::Page
* > & pageSet
, int setupFlags
)
739 bool documentChanged
= setupFlags
& Okular::DocumentObserver::DocumentChanged
;
740 // reuse current pages if nothing new
741 if ( ( pageSet
.count() == d
->items
.count() ) && !documentChanged
&& !( setupFlags
& Okular::DocumentObserver::NewLayoutForPages
) )
743 int count
= pageSet
.count();
744 for ( int i
= 0; (i
< count
) && !documentChanged
; i
++ )
745 if ( (int)pageSet
[i
]->number() != d
->items
[i
]->pageNumber() )
746 documentChanged
= true;
747 if ( !documentChanged
)
751 // delete all widgets (one for each page in pageSet)
752 QVector
< PageViewItem
* >::const_iterator dIt
= d
->items
.constBegin(), dEnd
= d
->items
.constEnd();
753 for ( ; dIt
!= dEnd
; ++dIt
)
756 d
->visibleItems
.clear();
757 d
->pagesWithTextSelection
.clear();
758 toggleFormWidgets( false );
759 if ( d
->formsWidgetController
)
760 d
->formsWidgetController
->dropRadioButtons();
762 bool haspages
= !pageSet
.isEmpty();
763 bool hasformwidgets
= false;
764 // create children widgets
765 QVector
< Okular::Page
* >::const_iterator setIt
= pageSet
.constBegin(), setEnd
= pageSet
.constEnd();
766 for ( ; setIt
!= setEnd
; ++setIt
)
768 PageViewItem
* item
= new PageViewItem( *setIt
);
769 d
->items
.push_back( item
);
770 #ifdef PAGEVIEW_DEBUG
771 kDebug().nospace() << "cropped geom for " << d
->items
.last()->pageNumber() << " is " << d
->items
.last()->croppedGeometry();
773 const QLinkedList
< Okular::FormField
* > pageFields
= (*setIt
)->formFields();
774 QLinkedList
< Okular::FormField
* >::const_iterator ffIt
= pageFields
.constBegin(), ffEnd
= pageFields
.constEnd();
775 for ( ; ffIt
!= ffEnd
; ++ffIt
)
777 Okular::FormField
* ff
= *ffIt
;
778 FormWidgetIface
* w
= FormWidgetFactory::createWidget( ff
, widget() );
781 w
->setPageItem( item
);
782 w
->setFormWidgetsController( d
->formWidgetsController() );
783 w
->setVisibility( false );
784 w
->setCanBeFilled( d
->document
->isAllowed( Okular::AllowFillForms
) );
785 item
->formWidgets().insert( ff
->id(), w
);
786 hasformwidgets
= true;
789 const QLinkedList
< Okular::Annotation
* > annotations
= (*setIt
)->annotations();
790 QLinkedList
< Okular::Annotation
* >::const_iterator aIt
= annotations
.constBegin(), aEnd
= annotations
.constEnd();
791 for ( ; aIt
!= aEnd
; ++aIt
)
793 Okular::Annotation
* a
= *aIt
;
794 if ( a
->subType() == Okular::Annotation::AMovie
)
796 Okular::MovieAnnotation
* movieAnn
= static_cast< Okular::MovieAnnotation
* >( a
);
797 VideoWidget
* vw
= new VideoWidget( movieAnn
, d
->document
, widget() );
798 item
->videoWidgets().insert( movieAnn
->movie(), vw
);
804 // invalidate layout so relayout/repaint will happen on next viewport change
806 // TODO for Enrico: Check if doing always the slotRelayoutPages() is not
807 // suboptimal in some cases, i'd say it is not but a recheck will not hurt
808 // Need slotRelayoutPages() here instead of d->dirtyLayout = true
809 // because opening a document from another document will not trigger a viewportchange
810 // so pages are never relayouted
811 QMetaObject::invokeMethod(this, "slotRelayoutPages", Qt::QueuedConnection
);
814 // update the mouse cursor when closing because we may have close through a link and
815 // want the cursor to come back to the normal cursor
816 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
817 setWidgetResizable(true);
820 // OSD to display pages
821 if ( documentChanged
&& pageSet
.count() > 0 && Okular::Settings::showOSD() )
822 d
->messageWindow
->display(
823 i18np(" Loaded a one-page document.",
824 " Loaded a %1-page document.",
826 PageViewMessage::Info
, 4000 );
829 { // may be null if dummy mode is on
830 bool pageSizes
= d
->document
->supportsPageSizes();
831 d
->aPageSizes
->setEnabled( pageSizes
);
832 // set the new page sizes:
833 // - if the generator supports them
834 // - if the document changed
835 if ( pageSizes
&& documentChanged
)
838 foreach ( const Okular::PageSize
&p
, d
->document
->pageSizes() )
839 items
.append( p
.name() );
840 d
->aPageSizes
->setItems( items
);
843 if ( d
->aRotateClockwise
)
844 d
->aRotateClockwise
->setEnabled( haspages
);
845 if ( d
->aRotateCounterClockwise
)
846 d
->aRotateCounterClockwise
->setEnabled( haspages
);
847 if ( d
->aRotateOriginal
)
848 d
->aRotateOriginal
->setEnabled( haspages
);
849 if ( d
->aToggleForms
)
850 { // may be null if dummy mode is on
851 d
->aToggleForms
->setEnabled( haspages
&& hasformwidgets
);
853 bool allowAnnotations
= d
->document
->isAllowed( Okular::AllowNotes
);
856 bool allowTools
= haspages
&& allowAnnotations
;
857 d
->annotator
->setToolsEnabled( allowTools
);
858 d
->annotator
->setTextToolsEnabled( allowTools
&& d
->document
->supportsSearching() );
860 if ( d
->aToggleAnnotator
)
862 if ( !allowAnnotations
&& d
->aToggleAnnotator
->isChecked() )
864 d
->aToggleAnnotator
->trigger();
866 d
->aToggleAnnotator
->setEnabled( allowAnnotations
);
870 const bool enablettsactions
= haspages
? Okular::Settings::useKTTSD() : false;
871 d
->aSpeakDoc
->setEnabled( enablettsactions
);
872 d
->aSpeakPage
->setEnabled( enablettsactions
);
876 void PageView::notifyViewportChanged( bool smoothMove
)
878 // if we are the one changing viewport, skip this nofity
879 if ( d
->blockViewport
)
882 // block setViewport outgoing calls
883 d
->blockViewport
= true;
885 // find PageViewItem matching the viewport description
886 const Okular::DocumentViewport
& vp
= d
->document
->viewport();
887 PageViewItem
* item
= 0;
888 QVector
< PageViewItem
* >::const_iterator iIt
= d
->items
.constBegin(), iEnd
= d
->items
.constEnd();
889 for ( ; iIt
!= iEnd
; ++iIt
)
890 if ( (*iIt
)->pageNumber() == vp
.pageNumber
)
897 kWarning() << "viewport for page" << vp
.pageNumber
<< "has no matching item!";
898 d
->blockViewport
= false;
901 #ifdef PAGEVIEW_DEBUG
902 kDebug() << "document viewport changed";
904 // relayout in "Single Pages" mode or if a relayout is pending
905 d
->blockPixmapsRequest
= true;
906 if ( !Okular::Settings::viewContinuous() || d
->dirtyLayout
)
909 // restore viewport center or use default {x-center,v-top} alignment
910 const QRect
& r
= item
->croppedGeometry();
911 int newCenterX
= r
.left(),
912 newCenterY
= r
.top();
913 if ( vp
.rePos
.enabled
)
915 if ( vp
.rePos
.pos
== Okular::DocumentViewport::Center
)
917 newCenterX
+= (int)( vp
.rePos
.normalizedX
* (double)r
.width() );
918 newCenterY
+= (int)( vp
.rePos
.normalizedY
* (double)r
.height() );
923 newCenterX
+= (int)( vp
.rePos
.normalizedX
* (double)r
.width() + viewport()->width() / 2 );
924 newCenterY
+= (int)( vp
.rePos
.normalizedY
* (double)r
.height() + viewport()->height() / 2 );
929 newCenterX
+= r
.width() / 2;
930 newCenterY
+= viewport()->height() / 2 - 10;
933 // if smooth movement requested, setup parameters and start it
936 d
->viewportMoveActive
= true;
937 d
->viewportMoveTime
.start();
938 d
->viewportMoveDest
.setX( newCenterX
);
939 d
->viewportMoveDest
.setY( newCenterY
);
940 if ( !d
->viewportMoveTimer
)
942 d
->viewportMoveTimer
= new QTimer( this );
943 connect( d
->viewportMoveTimer
, SIGNAL( timeout() ),
944 this, SLOT( slotMoveViewport() ) );
946 d
->viewportMoveTimer
->start( 25 );
947 verticalScrollBar()->setEnabled( false );
948 horizontalScrollBar()->setEnabled( false );
951 center( newCenterX
, newCenterY
);
952 d
->blockPixmapsRequest
= false;
954 // request visible pixmaps in the current viewport and recompute it
955 slotRequestVisiblePixmaps();
957 // enable setViewport calls
958 d
->blockViewport
= false;
960 // update zoom text if in a ZoomFit/* zoom mode
961 if ( d
->zoomMode
!= ZoomFixed
)
964 // since the page has moved below cursor, update it
965 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
968 void PageView::notifyPageChanged( int pageNumber
, int changedFlags
)
970 // only handle pixmap / highlight changes notifies
971 if ( changedFlags
& DocumentObserver::Bookmark
)
974 if ( changedFlags
& DocumentObserver::Annotations
)
976 const QLinkedList
< Okular::Annotation
* > annots
= d
->document
->page( pageNumber
)->annotations();
977 const QLinkedList
< Okular::Annotation
* >::ConstIterator annItEnd
= annots
.end();
978 QHash
< Okular::Annotation
*, AnnotWindow
* >::Iterator it
= d
->m_annowindows
.begin();
979 for ( ; it
!= d
->m_annowindows
.end(); )
981 QLinkedList
< Okular::Annotation
* >::ConstIterator annIt
= qFind( annots
, it
.key() );
982 if ( annIt
!= annItEnd
)
990 it
= d
->m_annowindows
.erase( it
);
995 if ( changedFlags
& DocumentObserver::BoundingBox
)
997 #ifdef PAGEVIEW_DEBUG
998 kDebug() << "BoundingBox change on page" << pageNumber
;
1000 slotRelayoutPages();
1001 slotRequestVisiblePixmaps(); // TODO: slotRelayoutPages() may have done this already!
1002 // Repaint the whole widget since layout may have changed
1007 // iterate over visible items: if page(pageNumber) is one of them, repaint it
1008 QLinkedList
< PageViewItem
* >::const_iterator iIt
= d
->visibleItems
.constBegin(), iEnd
= d
->visibleItems
.constEnd();
1009 for ( ; iIt
!= iEnd
; ++iIt
)
1010 if ( (*iIt
)->pageNumber() == pageNumber
&& (*iIt
)->isVisible() )
1012 // update item's rectangle plus the little outline
1013 QRect expandedRect
= (*iIt
)->croppedGeometry();
1014 expandedRect
.adjust( -1, -1, 3, 3 );
1015 widget()->update( expandedRect
);
1017 // if we were "zoom-dragging" do not overwrite the "zoom-drag" cursor
1018 if ( cursor().shape() != Qt::SizeVerCursor
)
1020 // since the page has been regenerated below cursor, update it
1021 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
1027 void PageView::notifyContentsCleared( int changedFlags
)
1029 // if pixmaps were cleared, re-ask them
1030 if ( changedFlags
& DocumentObserver::Pixmap
)
1031 QMetaObject::invokeMethod(this, "slotRequestVisiblePixmaps", Qt::QueuedConnection
);
1034 void PageView::notifyZoom( int factor
)
1037 updateZoom( ZoomIn
);
1039 updateZoom( ZoomOut
);
1042 bool PageView::canUnloadPixmap( int pageNumber
) const
1044 if ( Okular::Settings::memoryLevel() != Okular::Settings::EnumMemoryLevel::Aggressive
)
1046 // if the item is visible, forbid unloading
1047 QLinkedList
< PageViewItem
* >::const_iterator vIt
= d
->visibleItems
.constBegin(), vEnd
= d
->visibleItems
.constEnd();
1048 for ( ; vIt
!= vEnd
; ++vIt
)
1049 if ( (*vIt
)->pageNumber() == pageNumber
)
1054 // forbid unloading of the visible items, and of the previous and next
1055 QLinkedList
< PageViewItem
* >::const_iterator vIt
= d
->visibleItems
.constBegin(), vEnd
= d
->visibleItems
.constEnd();
1056 for ( ; vIt
!= vEnd
; ++vIt
)
1057 if ( abs( (*vIt
)->pageNumber() - pageNumber
) <= 1 )
1060 // if hidden premit unloading
1063 //END DocumentObserver inherited methods
1065 //BEGIN View inherited methods
1066 bool PageView::supportsCapability( ViewCapability capability
) const
1068 switch ( capability
)
1077 Okular::View::CapabilityFlags
PageView::capabilityFlags( ViewCapability capability
) const
1079 switch ( capability
)
1083 return CapabilityRead
| CapabilityWrite
| CapabilitySerializable
;
1088 QVariant
PageView::capability( ViewCapability capability
) const
1090 switch ( capability
)
1093 return d
->zoomFactor
;
1100 void PageView::setCapability( ViewCapability capability
, const QVariant
&option
)
1102 switch ( capability
)
1107 double factor
= option
.toDouble( &ok
);
1108 if ( ok
&& factor
> 0.0 )
1110 d
->zoomFactor
= static_cast< float >( factor
);
1111 updateZoom( ZoomRefreshCurrent
);
1118 int mode
= option
.toInt( &ok
);
1121 if ( mode
>= 0 && mode
< 3 )
1122 updateZoom( (ZoomMode
)mode
);
1129 //END View inherited methods
1131 //BEGIN widget events
1132 void PageView::contentsPaintEvent(QPaintEvent
*pe
)
1134 // create the rect into contents from the clipped screen rect
1135 QRect viewportRect
= viewport()->rect();
1136 viewportRect
.translate( horizontalScrollBar()->value(), verticalScrollBar()->value() );
1137 QRect contentsRect
= pe
->rect().intersect( viewportRect
);
1138 if ( !contentsRect
.isValid() )
1141 #ifdef PAGEVIEW_DEBUG
1142 kDebug() << "paintevent" << contentsRect
;
1145 // create the screen painter. a pixel painted at contentsX,contentsY
1146 // appears to the top-left corner of the scrollview.
1147 QPainter
screenPainter( widget() );
1149 // selectionRect is the normalized mouse selection rect
1150 QRect selectionRect
= d
->mouseSelectionRect
;
1151 if ( !selectionRect
.isNull() )
1152 selectionRect
= selectionRect
.normalized();
1153 // selectionRectInternal without the border
1154 QRect selectionRectInternal
= selectionRect
;
1155 selectionRectInternal
.adjust( 1, 1, -1, -1 );
1156 // color for blending
1157 QColor selBlendColor
= (selectionRect
.width() > 8 || selectionRect
.height() > 8) ?
1158 d
->mouseSelectionColor
: Qt::red
;
1160 // subdivide region into rects
1161 const QVector
<QRect
> &allRects
= pe
->region().rects();
1162 uint numRects
= allRects
.count();
1164 // preprocess rects area to see if it worths or not using subdivision
1165 uint summedArea
= 0;
1166 for ( uint i
= 0; i
< numRects
; i
++ )
1168 const QRect
& r
= allRects
[i
];
1169 summedArea
+= r
.width() * r
.height();
1171 // very elementary check: SUMj(Region[j].area) is less than boundingRect.area
1172 bool useSubdivision
= summedArea
< (0.6 * contentsRect
.width() * contentsRect
.height());
1173 if ( !useSubdivision
)
1176 // iterate over the rects (only one loop if not using subdivision)
1177 for ( uint i
= 0; i
< numRects
; i
++ )
1179 if ( useSubdivision
)
1181 // set 'contentsRect' to a part of the sub-divided region
1182 contentsRect
= allRects
[i
].normalized().intersect( viewportRect
);
1183 if ( !contentsRect
.isValid() )
1186 #ifdef PAGEVIEW_DEBUG
1187 kDebug() << contentsRect
;
1190 // note: this check will take care of all things requiring alpha blending (not only selection)
1191 bool wantCompositing
= !selectionRect
.isNull() && contentsRect
.intersects( selectionRect
);
1193 if ( wantCompositing
&& Okular::Settings::enableCompositing() )
1195 // create pixmap and open a painter over it (contents{left,top} becomes pixmap {0,0})
1196 QPixmap
doubleBuffer( contentsRect
.size() );
1197 QPainter
pixmapPainter( &doubleBuffer
);
1198 pixmapPainter
.translate( -contentsRect
.left(), -contentsRect
.top() );
1200 // 1) Layer 0: paint items and clear bg on unpainted rects
1201 drawDocumentOnPainter( contentsRect
, &pixmapPainter
);
1202 // 2) Layer 1a: paint (blend) transparent selection
1203 if ( !selectionRect
.isNull() && selectionRect
.intersects( contentsRect
) &&
1204 !selectionRectInternal
.contains( contentsRect
) )
1206 QRect blendRect
= selectionRectInternal
.intersect( contentsRect
);
1207 // skip rectangles covered by the selection's border
1208 if ( blendRect
.isValid() )
1210 // grab current pixmap into a new one to colorize contents
1211 QPixmap
blendedPixmap( blendRect
.width(), blendRect
.height() );
1212 QPainter
p( &blendedPixmap
);
1213 p
.drawPixmap( 0, 0, doubleBuffer
,
1214 blendRect
.left() - contentsRect
.left(), blendRect
.top() - contentsRect
.top(),
1215 blendRect
.width(), blendRect
.height() );
1217 QColor blCol
= selBlendColor
.dark( 140 );
1218 blCol
.setAlphaF( 0.2 );
1219 p
.fillRect( blendedPixmap
.rect(), blCol
);
1221 // copy the blended pixmap back to its place
1222 pixmapPainter
.drawPixmap( blendRect
.left(), blendRect
.top(), blendedPixmap
);
1224 // draw border (red if the selection is too small)
1225 pixmapPainter
.setPen( selBlendColor
);
1226 pixmapPainter
.drawRect( selectionRect
.adjusted( 0, 0, -1, -1 ) );
1228 // 3) Layer 1: give annotator painting control
1229 if ( d
->annotator
&& d
->annotator
->routePaints( contentsRect
) )
1230 d
->annotator
->routePaint( &pixmapPainter
, contentsRect
);
1231 // 4) Layer 2: overlays
1232 if ( Okular::Settings::debugDrawBoundaries() )
1234 pixmapPainter
.setPen( Qt::blue
);
1235 pixmapPainter
.drawRect( contentsRect
);
1238 // finish painting and draw contents
1239 pixmapPainter
.end();
1240 screenPainter
.drawPixmap( contentsRect
.left(), contentsRect
.top(), doubleBuffer
);
1244 // 1) Layer 0: paint items and clear bg on unpainted rects
1245 drawDocumentOnPainter( contentsRect
, &screenPainter
);
1246 // 2) Layer 1: paint opaque selection
1247 if ( !selectionRect
.isNull() && selectionRect
.intersects( contentsRect
) &&
1248 !selectionRectInternal
.contains( contentsRect
) )
1250 screenPainter
.setPen( palette().color( QPalette::Active
, QPalette::Highlight
).dark(110) );
1251 screenPainter
.drawRect( selectionRect
);
1253 // 3) Layer 1: give annotator painting control
1254 if ( d
->annotator
&& d
->annotator
->routePaints( contentsRect
) )
1255 d
->annotator
->routePaint( &screenPainter
, contentsRect
);
1256 // 4) Layer 2: overlays
1257 if ( Okular::Settings::debugDrawBoundaries() )
1259 screenPainter
.setPen( Qt::red
);
1260 screenPainter
.drawRect( contentsRect
);
1266 void PageView::resizeEvent( QResizeEvent
*e
)
1268 if ( d
->items
.isEmpty() )
1270 widget()->resize(e
->size());
1274 if ( d
->zoomMode
== ZoomFitWidth
&& d
->bothScrollbarsVisible
&& !horizontalScrollBar()->isVisible() && !verticalScrollBar()->isVisible() && qAbs(e
->oldSize().height() - e
->size().height()) < horizontalScrollBar()->height() * 1.25 )
1276 // this saves us from infinite resizing loop because of scrollbars appearing and disappearing
1277 // see bug 160628 for more info
1278 // TODO looks are still a bit ugly because things are left uncentered
1279 // but better a bit ugly than unusable
1280 d
->bothScrollbarsVisible
= false;
1281 widget()->resize( e
->size() );
1285 // start a timer that will refresh the pixmap after 0.2s
1286 if ( !d
->delayResizeTimer
)
1288 d
->delayResizeTimer
= new QTimer( this );
1289 d
->delayResizeTimer
->setSingleShot( true );
1290 connect( d
->delayResizeTimer
, SIGNAL( timeout() ), this, SLOT( slotRelayoutPages() ) );
1292 d
->delayResizeTimer
->start( 200 );
1294 d
->bothScrollbarsVisible
= horizontalScrollBar()->isVisible() && verticalScrollBar()->isVisible();
1297 void PageView::keyPressEvent( QKeyEvent
* e
)
1301 // if performing a selection or dyn zooming, disable keys handling
1302 if ( ( d
->mouseSelecting
&& e
->key() != Qt::Key_Escape
) || d
->mouseMidZooming
)
1305 // if viewport is moving, disable keys handling
1306 if ( d
->viewportMoveActive
)
1309 // move/scroll page by using keys
1315 case Qt::Key_PageDown
:
1318 case Qt::Key_PageUp
:
1319 case Qt::Key_Backspace
:
1320 if ( e
->key() == Qt::Key_Down
1321 || e
->key() == Qt::Key_PageDown
1322 || e
->key() == Qt::Key_J
1323 || ( e
->key() == Qt::Key_Space
&& ( e
->modifiers() & Qt::ShiftModifier
) != Qt::ShiftModifier
) )
1325 // if in single page mode and at the bottom of the screen, go to next page
1326 if ( Okular::Settings::viewContinuous() || verticalScrollBar()->value() < verticalScrollBar()->maximum() )
1328 if ( e
->key() == Qt::Key_Down
|| e
->key() == Qt::Key_J
)
1329 verticalScrollBar()->triggerAction( QScrollBar::SliderSingleStepAdd
);
1331 verticalScrollBar()->triggerAction( QScrollBar::SliderPageStepAdd
);
1333 else if ( (int)d
->document
->currentPage() < d
->items
.count() - 1 )
1335 // more optimized than document->setNextPage and then move view to top
1336 Okular::DocumentViewport newViewport
= d
->document
->viewport();
1337 newViewport
.pageNumber
+= d
->document
->currentPage() ? viewColumns() : 1;
1338 if ( newViewport
.pageNumber
>= (int)d
->items
.count() )
1339 newViewport
.pageNumber
= d
->items
.count() - 1;
1340 newViewport
.rePos
.enabled
= true;
1341 newViewport
.rePos
.normalizedY
= 0.0;
1342 d
->document
->setViewport( newViewport
);
1347 // if in single page mode and at the top of the screen, go to \ page
1348 if ( Okular::Settings::viewContinuous() || verticalScrollBar()->value() > verticalScrollBar()->minimum() )
1350 if ( e
->key() == Qt::Key_Up
|| e
->key() == Qt::Key_K
)
1351 verticalScrollBar()->triggerAction( QScrollBar::SliderSingleStepSub
);
1353 verticalScrollBar()->triggerAction( QScrollBar::SliderPageStepSub
);
1355 else if ( d
->document
->currentPage() > 0 )
1357 // more optimized than document->setPrevPage and then move view to bottom
1358 Okular::DocumentViewport newViewport
= d
->document
->viewport();
1359 newViewport
.pageNumber
-= viewColumns();
1360 if ( newViewport
.pageNumber
< 0 )
1361 newViewport
.pageNumber
= 0;
1362 newViewport
.rePos
.enabled
= true;
1363 newViewport
.rePos
.normalizedY
= 1.0;
1364 d
->document
->setViewport( newViewport
);
1370 horizontalScrollBar()->triggerAction( QScrollBar::SliderSingleStepSub
);
1374 horizontalScrollBar()->triggerAction( QScrollBar::SliderSingleStepAdd
);
1376 case Qt::Key_Escape
:
1378 d
->mousePressPos
= QPoint();
1379 if ( d
->aPrevAction
)
1381 d
->aPrevAction
->trigger();
1386 case Qt::Key_Control
:
1387 if ( d
->autoScrollTimer
)
1389 if ( d
->autoScrollTimer
->isActive() )
1390 d
->autoScrollTimer
->stop();
1395 // else fall trhough
1400 // if a known key has been pressed, stop scrolling the page
1401 if ( d
->autoScrollTimer
)
1403 d
->scrollIncrement
= 0;
1404 d
->autoScrollTimer
->stop();
1408 void PageView::keyReleaseEvent( QKeyEvent
* e
)
1412 if ( d
->annotator
&& d
->annotator
->routeEvents() )
1414 if ( d
->annotator
->routeKeyEvent( e
) )
1418 if ( e
->key() == Qt::Key_Escape
&& d
->autoScrollTimer
)
1420 d
->scrollIncrement
= 0;
1421 d
->autoScrollTimer
->stop();
1425 void PageView::inputMethodEvent( QInputMethodEvent
* e
)
1430 static QPoint
rotateInRect( const QPoint
&rotated
, Okular::Rotation rotation
)
1436 case Okular::Rotation90
:
1437 ret
= QPoint( rotated
.y(), -rotated
.x() );
1439 case Okular::Rotation180
:
1440 ret
= QPoint( -rotated
.x(), -rotated
.y() );
1442 case Okular::Rotation270
:
1443 ret
= QPoint( -rotated
.y(), rotated
.x() );
1445 case Okular::Rotation0
: // no modifications
1446 default: // other cases
1453 void PageView::contentsMouseMoveEvent( QMouseEvent
* e
)
1455 // don't perform any mouse action when no document is shown
1456 if ( d
->items
.isEmpty() )
1459 // don't perform any mouse action when viewport is autoscrolling
1460 if ( d
->viewportMoveActive
)
1463 // if holding mouse mid button, perform zoom
1464 if ( d
->mouseMidZooming
&& (e
->buttons() & Qt::MidButton
) )
1466 int mouseY
= e
->globalPos().y();
1467 int deltaY
= d
->mouseMidLastY
- mouseY
;
1469 // wrap mouse from top to bottom
1470 QRect mouseContainer
= KGlobalSettings::desktopGeometry( this );
1471 if ( mouseY
<= mouseContainer
.top() + 4 &&
1472 d
->zoomFactor
< 3.99 )
1474 mouseY
= mouseContainer
.bottom() - 5;
1475 QCursor::setPos( e
->globalPos().x(), mouseY
);
1477 // wrap mouse from bottom to top
1478 else if ( mouseY
>= mouseContainer
.bottom() - 4 &&
1479 d
->zoomFactor
> 0.11 )
1481 mouseY
= mouseContainer
.top() + 5;
1482 QCursor::setPos( e
->globalPos().x(), mouseY
);
1484 // remember last position
1485 d
->mouseMidLastY
= mouseY
;
1487 // update zoom level, perform zoom and redraw
1490 d
->zoomFactor
*= ( 1.0 + ( (double)deltaY
/ 500.0 ) );
1491 updateZoom( ZoomRefreshCurrent
);
1492 viewport()->repaint();
1497 // if we're editing an annotation, dispatch event to it
1498 if ( d
->annotator
&& d
->annotator
->routeEvents() )
1500 PageViewItem
* pageItem
= pickItemOnPoint( e
->x(), e
->y() );
1501 d
->annotator
->routeEvent( e
, pageItem
);
1505 bool leftButton
= (e
->buttons() == Qt::LeftButton
);
1506 bool rightButton
= (e
->buttons() == Qt::RightButton
);
1507 switch ( d
->mouseMode
)
1514 PageViewItem
* pageItem
= pickItemOnPoint( e
->x(), e
->y() );
1517 const QRect
& itemRect
= pageItem
->uncroppedGeometry();
1518 QPoint newpos
= QPoint( e
->x(), e
->y() ) - itemRect
.topLeft();
1519 Okular::NormalizedRect r
= d
->mouseAnn
->boundingRectangle();
1520 QPoint
p( newpos
- d
->mouseAnnPos
);
1521 QPointF
pf( rotateInRect( p
, pageItem
->page()->rotation() ) );
1522 if ( pageItem
->page()->rotation() % 2 == 0 )
1524 pf
.rx() /= pageItem
->uncroppedWidth();
1525 pf
.ry() /= pageItem
->uncroppedHeight();
1529 pf
.rx() /= pageItem
->uncroppedHeight();
1530 pf
.ry() /= pageItem
->uncroppedWidth();
1532 d
->mouseAnn
->translate( Okular::NormalizedPoint( pf
.x(), pf
.y() ) );
1533 d
->mouseAnnPos
= newpos
;
1534 d
->document
->modifyPageAnnotation( pageItem
->pageNumber(), d
->mouseAnn
);
1538 else if ( !d
->mouseGrabPos
.isNull() )
1540 QPoint mousePos
= e
->globalPos();
1541 QPoint delta
= d
->mouseGrabPos
- mousePos
;
1543 // wrap mouse from top to bottom
1544 QRect mouseContainer
= KGlobalSettings::desktopGeometry( this );
1545 if ( mousePos
.y() <= mouseContainer
.top() + 4 &&
1546 verticalScrollBar()->value() < verticalScrollBar()->maximum() - 10 )
1548 mousePos
.setY( mouseContainer
.bottom() - 5 );
1549 QCursor::setPos( mousePos
);
1551 // wrap mouse from bottom to top
1552 else if ( mousePos
.y() >= mouseContainer
.bottom() - 4 &&
1553 verticalScrollBar()->value() > 10 )
1555 mousePos
.setY( mouseContainer
.top() + 5 );
1556 QCursor::setPos( mousePos
);
1558 // remember last position
1559 d
->mouseGrabPos
= mousePos
;
1561 // scroll page by position increment
1562 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + delta
.x());
1563 verticalScrollBar()->setValue(verticalScrollBar()->value() + delta
.y());
1566 else if ( rightButton
&& !d
->mousePressPos
.isNull() )
1568 // if mouse moves 5 px away from the press point, switch to 'selection'
1569 int deltaX
= d
->mousePressPos
.x() - e
->globalPos().x(),
1570 deltaY
= d
->mousePressPos
.y() - e
->globalPos().y();
1571 if ( deltaX
> 5 || deltaX
< -5 || deltaY
> 5 || deltaY
< -5 )
1573 d
->aPrevAction
= d
->aMouseNormal
;
1574 d
->aMouseSelect
->trigger();
1575 QPoint
newPos(e
->x() + deltaX
, e
->y() + deltaY
);
1576 selectionStart( newPos
, palette().color( QPalette::Active
, QPalette::Highlight
).light( 120 ), false );
1577 selectionEndPoint( e
->pos() );
1583 // only hovering the page, so update the cursor
1584 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
1590 case MouseImageSelect
:
1591 // set second corner of selection
1592 if ( d
->mouseSelecting
)
1593 selectionEndPoint( e
->pos() );
1595 case MouseTextSelect
:
1596 // if mouse moves 5 px away from the press point and the document soupports text extraction, do 'textselection'
1597 if ( !d
->mouseTextSelecting
&& !d
->mousePressPos
.isNull() && d
->document
->supportsSearching() && ( ( e
->pos() - d
->mouseSelectPos
).manhattanLength() > 5 ) )
1599 d
->mouseTextSelecting
= true;
1601 if ( d
->mouseTextSelecting
)
1604 QList
< Okular::RegularAreaRect
* > selections
= textSelections( e
->pos(), d
->mouseSelectPos
, first
);
1605 QSet
< int > pagesWithSelectionSet
;
1606 for ( int i
= 0; i
< selections
.count(); ++i
)
1607 pagesWithSelectionSet
.insert( i
+ first
);
1609 QSet
< int > noMoreSelectedPages
= d
->pagesWithTextSelection
- pagesWithSelectionSet
;
1610 // clear the selection from pages not selected anymore
1611 foreach( int p
, noMoreSelectedPages
)
1613 d
->document
->setPageTextSelection( p
, 0, QColor() );
1615 // set the new selection for the selected pages
1616 foreach( int p
, pagesWithSelectionSet
)
1618 d
->document
->setPageTextSelection( p
, selections
[ p
- first
], palette().color( QPalette::Active
, QPalette::Highlight
) );
1620 d
->pagesWithTextSelection
= pagesWithSelectionSet
;
1622 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
1627 void PageView::contentsMousePressEvent( QMouseEvent
* e
)
1629 // don't perform any mouse action when no document is shown
1630 if ( d
->items
.isEmpty() )
1633 // if performing a selection or dyn zooming, disable mouse press
1634 if ( d
->mouseSelecting
|| d
->mouseMidZooming
|| d
->viewportMoveActive
)
1637 // if the page is scrolling, stop it
1638 if ( d
->autoScrollTimer
)
1640 d
->scrollIncrement
= 0;
1641 d
->autoScrollTimer
->stop();
1644 // if pressing mid mouse button while not doing other things, begin 'continuous zoom' mode
1645 if ( e
->button() == Qt::MidButton
)
1647 d
->mouseMidZooming
= true;
1648 d
->mouseMidLastY
= e
->globalPos().y();
1649 setCursor( Qt::SizeVerCursor
);
1653 // if we're editing an annotation, dispatch event to it
1654 if ( d
->annotator
&& d
->annotator
->routeEvents() )
1656 PageViewItem
* pageItem
= pickItemOnPoint( e
->x(), e
->y() );
1657 d
->annotator
->routeEvent( e
, pageItem
);
1661 // update press / 'start drag' mouse position
1662 d
->mousePressPos
= e
->globalPos();
1664 // handle mode dependant mouse press actions
1665 bool leftButton
= e
->button() == Qt::LeftButton
,
1666 rightButton
= e
->button() == Qt::RightButton
;
1668 // Not sure we should erase the selection when clicking with left.
1669 if ( d
->mouseMode
!= MouseTextSelect
)
1670 textSelectionClear();
1672 switch ( d
->mouseMode
)
1674 case MouseNormal
: // drag start / click / link following
1677 PageViewItem
* pageItem
= 0;
1678 if ( ( e
->modifiers() & Qt::ControlModifier
) && ( pageItem
= pickItemOnPoint( e
->x(), e
->y() ) ) )
1680 // find out normalized mouse coords inside current item
1681 const QRect
& itemRect
= pageItem
->uncroppedGeometry();
1682 double nX
= pageItem
->absToPageX(e
->x());
1683 double nY
= pageItem
->absToPageY(e
->y());
1684 const Okular::ObjectRect
* orect
= pageItem
->page()->objectRect( Okular::ObjectRect::OAnnotation
, nX
, nY
, itemRect
.width(), itemRect
.height() );
1685 d
->mouseAnnPos
= QPoint( e
->x(), e
->y() ) - itemRect
.topLeft();
1687 d
->mouseAnn
= ( (Okular::AnnotationObjectRect
*)orect
)->annotation();
1688 // consider no annotation caught if its type is not movable
1689 if ( d
->mouseAnn
&& !d
->mouseAnn
->canBeMoved() )
1694 d
->mouseGrabPos
= d
->mouseOnRect
? QPoint() : d
->mousePressPos
;
1695 if ( !d
->mouseOnRect
)
1696 setCursor( Qt::SizeAllCursor
);
1699 else if ( rightButton
)
1701 PageViewItem
* pageItem
= pickItemOnPoint( e
->x(), e
->y() );
1704 // find out normalized mouse coords inside current item
1705 const QRect
& itemRect
= pageItem
->uncroppedGeometry();
1706 double nX
= pageItem
->absToPageX(e
->x());
1707 double nY
= pageItem
->absToPageY(e
->y());
1708 Okular::Annotation
* ann
= 0;
1709 const Okular::ObjectRect
* orect
= pageItem
->page()->objectRect( Okular::ObjectRect::OAnnotation
, nX
, nY
, itemRect
.width(), itemRect
.height() );
1711 ann
= ( (Okular::AnnotationObjectRect
*)orect
)->annotation();
1714 AnnotationPopup
popup( d
->document
, this );
1715 popup
.addAnnotation( ann
, pageItem
->pageNumber() );
1717 connect( &popup
, SIGNAL( setAnnotationWindow( Okular::Annotation
* ) ),
1718 this, SLOT( setAnnotationWindow( Okular::Annotation
* ) ) );
1719 connect( &popup
, SIGNAL( removeAnnotationWindow( Okular::Annotation
* ) ),
1720 this, SLOT( removeAnnotationWindow( Okular::Annotation
* ) ) );
1722 popup
.exec( e
->globalPos() );
1728 case MouseZoom
: // set first corner of the zoom rect
1730 selectionStart( e
->pos(), palette().color( QPalette::Active
, QPalette::Highlight
), false );
1731 else if ( rightButton
)
1732 updateZoom( ZoomOut
);
1735 case MouseSelect
: // set first corner of the selection rect
1736 case MouseImageSelect
:
1739 selectionStart( e
->pos(), palette().color( QPalette::Active
, QPalette::Highlight
).light( 120 ), false );
1742 case MouseTextSelect
:
1743 d
->mouseSelectPos
= e
->pos();
1746 textSelectionClear();
1752 void PageView::contentsMouseReleaseEvent( QMouseEvent
* e
)
1754 // stop the drag scrolling
1755 d
->dragScrollTimer
.stop();
1757 // don't perform any mouse action when no document is shown..
1758 if ( d
->items
.isEmpty() )
1760 // ..except for right Clicks (emitted even it viewport is empty)
1761 if ( e
->button() == Qt::RightButton
)
1762 emit
rightClick( 0, e
->globalPos() );
1766 // don't perform any mouse action when viewport is autoscrolling
1767 if ( d
->viewportMoveActive
)
1770 // handle mode indepent mid buttom zoom
1771 if ( d
->mouseMidZooming
&& (e
->button() == Qt::MidButton
) )
1773 d
->mouseMidZooming
= false;
1774 // request pixmaps since it was disabled during drag
1775 slotRequestVisiblePixmaps();
1776 // the cursor may now be over a link.. update it
1777 updateCursor( e
->pos() );
1781 // if we're editing an annotation, dispatch event to it
1782 if ( d
->annotator
&& d
->annotator
->routeEvents() )
1784 PageViewItem
* pageItem
= pickItemOnPoint( e
->x(), e
->y() );
1785 d
->annotator
->routeEvent( e
, pageItem
);
1791 setCursor( Qt::ArrowCursor
);
1795 bool leftButton
= e
->button() == Qt::LeftButton
;
1796 bool rightButton
= e
->button() == Qt::RightButton
;
1797 switch ( d
->mouseMode
)
1800 // return the cursor to its normal state after dragging
1801 if ( cursor().shape() == Qt::SizeAllCursor
)
1802 updateCursor( e
->pos() );
1804 PageViewItem
* pageItem
= pickItemOnPoint( e
->x(), e
->y() );
1806 // if the mouse has not moved since the press, that's a -click-
1807 if ( leftButton
&& pageItem
&& d
->mousePressPos
== e
->globalPos())
1809 double nX
= pageItem
->absToPageX(e
->x());
1810 double nY
= pageItem
->absToPageY(e
->y());
1811 const Okular::ObjectRect
* rect
;
1812 rect
= pageItem
->page()->objectRect( Okular::ObjectRect::Action
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
1815 // handle click over a link
1816 const Okular::Action
* action
= static_cast< const Okular::Action
* >( rect
->object() );
1817 d
->document
->processAction( action
);
1821 // TODO: find a better way to activate the source reference "links"
1822 // for the moment they are activated with Shift + left click
1823 rect
= e
->modifiers() == Qt::ShiftModifier
? pageItem
->page()->objectRect( Okular::ObjectRect::SourceRef
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() ) : 0;
1826 const Okular::SourceReference
* ref
= static_cast< const Okular::SourceReference
* >( rect
->object() );
1827 d
->document
->processSourceReference( ref
);
1830 // a link can move us to another page or even to another document, there's no point in trying to
1831 // process the click on the image once we have processes the click on the link
1832 rect
= pageItem
->page()->objectRect( Okular::ObjectRect::Image
, nX
, nY
, pageItem
->width(), pageItem
->height() );
1835 // handle click over a image
1837 /* Enrico and me have decided this is not worth the trouble it generates
1840 // if not on a rect, the click selects the page
1841 // if ( pageItem->pageNumber() != (int)d->document->currentPage() )
1842 d->document->setViewportPage( pageItem->pageNumber(), PAGEVIEW_ID );
1847 else if ( rightButton
)
1849 if ( pageItem
&& d
->mousePressPos
== e
->globalPos() )
1851 double nX
= pageItem
->absToPageX(e
->x());
1852 double nY
= pageItem
->absToPageY(e
->y());
1853 const Okular::ObjectRect
* rect
;
1854 rect
= pageItem
->page()->objectRect( Okular::ObjectRect::Action
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
1857 // handle right click over a link
1858 const Okular::Action
* link
= static_cast< const Okular::Action
* >( rect
->object() );
1859 // creating the menu and its actions
1861 QAction
* actProcessLink
= menu
.addAction( i18n( "Follow This Link" ) );
1862 QAction
* actCopyLinkLocation
= 0;
1863 if ( dynamic_cast< const Okular::BrowseAction
* >( link
) )
1864 actCopyLinkLocation
= menu
.addAction( KIcon( "edit-copy" ), i18n( "Copy Link Address" ) );
1865 QAction
* res
= menu
.exec( e
->globalPos() );
1868 if ( res
== actProcessLink
)
1870 d
->document
->processAction( link
);
1872 else if ( res
== actCopyLinkLocation
)
1874 const Okular::BrowseAction
* browseLink
= static_cast< const Okular::BrowseAction
* >( link
);
1875 QClipboard
*cb
= QApplication::clipboard();
1876 cb
->setText( browseLink
->url(), QClipboard::Clipboard
);
1877 if ( cb
->supportsSelection() )
1878 cb
->setText( browseLink
->url(), QClipboard::Selection
);
1884 // a link can move us to another page or even to another document, there's no point in trying to
1885 // process the click on the image once we have processes the click on the link
1886 rect
= pageItem
->page()->objectRect( Okular::ObjectRect::Image
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
1889 // handle right click over a image
1893 // right click (if not within 5 px of the press point, the mode
1894 // had been already changed to 'Selection' instead of 'Normal')
1895 emit
rightClick( pageItem
->page(), e
->globalPos() );
1901 // right click (if not within 5 px of the press point, the mode
1902 // had been already changed to 'Selection' instead of 'Normal')
1903 emit
rightClick( pageItem
? pageItem
->page() : 0, e
->globalPos() );
1909 // if a selection rect has been defined, zoom into it
1910 if ( leftButton
&& d
->mouseSelecting
)
1912 QRect selRect
= d
->mouseSelectionRect
.normalized();
1913 if ( selRect
.width() <= 8 && selRect
.height() <= 8 )
1919 // find out new zoom ratio and normalized view center (relative to the contentsRect)
1920 double zoom
= qMin( (double)viewport()->width() / (double)selRect
.width(), (double)viewport()->height() / (double)selRect
.height() );
1921 double nX
= (double)(selRect
.left() + selRect
.right()) / (2.0 * (double)widget()->width());
1922 double nY
= (double)(selRect
.top() + selRect
.bottom()) / (2.0 * (double)widget()->height());
1925 if ( d
->zoomFactor
<= 4.0 || zoom
<= 1.0 )
1927 d
->zoomFactor
*= zoom
;
1928 viewport()->setUpdatesEnabled( false );
1929 updateZoom( ZoomRefreshCurrent
);
1930 viewport()->setUpdatesEnabled( true );
1933 // recenter view and update the viewport
1934 center( (int)(nX
* widget()->width()), (int)(nY
* widget()->height()) );
1937 // hide message box and delete overlay window
1943 case MouseImageSelect
:
1945 // if mouse is released and selection is null this is a rightClick
1946 if ( rightButton
&& !d
->mouseSelecting
)
1948 PageViewItem
* pageItem
= pickItemOnPoint( e
->x(), e
->y() );
1949 emit
rightClick( pageItem
? pageItem
->page() : 0, e
->globalPos() );
1953 // if a selection is defined, display a popup
1954 if ( (!leftButton
&& !d
->aPrevAction
) || (!rightButton
&& d
->aPrevAction
) ||
1955 !d
->mouseSelecting
)
1958 QRect selectionRect
= d
->mouseSelectionRect
.normalized();
1959 if ( selectionRect
.width() <= 8 && selectionRect
.height() <= 8 )
1962 if ( d
->aPrevAction
)
1964 d
->aPrevAction
->trigger();
1970 // if we support text generation
1971 QString selectedText
;
1972 if (d
->document
->supportsSearching())
1974 // grab text in selection by extracting it from all intersected pages
1975 const Okular::Page
* okularPage
=0;
1976 QVector
< PageViewItem
* >::const_iterator iIt
= d
->items
.constBegin(), iEnd
= d
->items
.constEnd();
1977 for ( ; iIt
!= iEnd
; ++iIt
)
1979 PageViewItem
* item
= *iIt
;
1980 if ( !item
->isVisible() )
1983 const QRect
& itemRect
= item
->croppedGeometry();
1984 if ( selectionRect
.intersects( itemRect
) )
1986 // request the textpage if there isn't one
1987 okularPage
= item
->page();
1988 kWarning() << "checking if page" << item
->pageNumber() << "has text:" << okularPage
->hasTextPage();
1989 if ( !okularPage
->hasTextPage() )
1990 d
->document
->requestTextPage( okularPage
->number() );
1991 // grab text in the rect that intersects itemRect
1992 QRect relativeRect
= selectionRect
.intersect( itemRect
);
1993 relativeRect
.translate( -item
->uncroppedGeometry().topLeft() );
1994 Okular::RegularAreaRect rects
;
1995 rects
.append( Okular::NormalizedRect( relativeRect
, item
->uncroppedWidth(), item
->uncroppedHeight() ) );
1996 selectedText
+= okularPage
->text( &rects
);
2001 // popup that ask to copy:text and copy/save:image
2003 QAction
*textToClipboard
= 0, *speakText
= 0, *imageToClipboard
= 0, *imageToFile
= 0;
2004 if ( d
->document
->supportsSearching() && !selectedText
.isEmpty() )
2006 menu
.addTitle( i18np( "Text (1 character)", "Text (%1 characters)", selectedText
.length() ) );
2007 textToClipboard
= menu
.addAction( KIcon("edit-copy"), i18n( "Copy to Clipboard" ) );
2008 if ( !d
->document
->isAllowed( Okular::AllowCopy
) )
2010 textToClipboard
->setEnabled( false );
2011 textToClipboard
->setText( i18n("Copy forbidden by DRM") );
2013 if ( Okular::Settings::useKTTSD() )
2014 speakText
= menu
.addAction( KIcon("text-speak"), i18n( "Speak Text" ) );
2016 menu
.addTitle( i18n( "Image (%1 by %2 pixels)", selectionRect
.width(), selectionRect
.height() ) );
2017 imageToClipboard
= menu
.addAction( KIcon("image-x-generic"), i18n( "Copy to Clipboard" ) );
2018 imageToFile
= menu
.addAction( KIcon("document-save"), i18n( "Save to File..." ) );
2019 QAction
*choice
= menu
.exec( e
->globalPos() );
2020 // check if the user really selected an action
2023 // IMAGE operation chosen
2024 if ( choice
== imageToClipboard
|| choice
== imageToFile
)
2026 // renders page into a pixmap
2027 QPixmap
copyPix( selectionRect
.width(), selectionRect
.height() );
2028 QPainter
copyPainter( ©Pix
);
2029 copyPainter
.translate( -selectionRect
.left(), -selectionRect
.top() );
2030 drawDocumentOnPainter( selectionRect
, ©Painter
);
2033 if ( choice
== imageToClipboard
)
2035 // [2] copy pixmap to clipboard
2036 QClipboard
*cb
= QApplication::clipboard();
2037 cb
->setPixmap( copyPix
, QClipboard::Clipboard
);
2038 if ( cb
->supportsSelection() )
2039 cb
->setPixmap( copyPix
, QClipboard::Selection
);
2040 d
->messageWindow
->display( i18n( "Image [%1x%2] copied to clipboard.", copyPix
.width(), copyPix
.height() ) );
2042 else if ( choice
== imageToFile
)
2044 // [3] save pixmap to file
2045 QString fileName
= KFileDialog::getSaveFileName( KUrl(), "image/png image/jpeg", this );
2046 if ( fileName
.isEmpty() )
2047 d
->messageWindow
->display( i18n( "File not saved." ), PageViewMessage::Warning
);
2050 KMimeType::Ptr mime
= KMimeType::findByUrl( fileName
);
2052 if ( !mime
|| mime
== KMimeType::defaultMimeTypePtr() )
2055 type
= mime
->name().section( '/', -1 ).toUpper();
2056 copyPix
.save( fileName
, qPrintable( type
) );
2057 d
->messageWindow
->display( i18n( "Image [%1x%2] saved to %3 file.", copyPix
.width(), copyPix
.height(), type
) );
2061 // TEXT operation chosen
2064 if ( choice
== textToClipboard
)
2066 // [1] copy text to clipboard
2067 QClipboard
*cb
= QApplication::clipboard();
2068 cb
->setText( selectedText
, QClipboard::Clipboard
);
2069 if ( cb
->supportsSelection() )
2070 cb
->setText( selectedText
, QClipboard::Selection
);
2072 else if ( choice
== speakText
)
2074 // [2] speech selection using KTTSD
2075 d
->tts()->say( selectedText
);
2079 // clear widget selection and invalidate rect
2082 // restore previous action if came from it using right button
2083 if ( d
->aPrevAction
)
2085 d
->aPrevAction
->trigger();
2089 case MouseTextSelect
:
2090 setCursor( Qt::ArrowCursor
);
2091 if ( d
->mouseTextSelecting
)
2093 d
->mouseTextSelecting
= false;
2094 // textSelectionClear();
2095 if ( d
->document
->isAllowed( Okular::AllowCopy
) )
2097 const QString text
= d
->selectedText();
2098 if ( !text
.isEmpty() )
2100 QClipboard
*cb
= QApplication::clipboard();
2101 if ( cb
->supportsSelection() )
2102 cb
->setText( text
, QClipboard::Selection
);
2106 else if ( !d
->mousePressPos
.isNull() && rightButton
)
2109 QAction
*textToClipboard
= menu
.addAction( KIcon( "edit-copy" ), i18n( "Copy Text" ) );
2110 QAction
*speakText
= 0;
2111 if ( Okular::Settings::useKTTSD() )
2112 speakText
= menu
.addAction( KIcon( "text-speak" ), i18n( "Speak Text" ) );
2113 if ( !d
->document
->isAllowed( Okular::AllowCopy
) )
2115 textToClipboard
->setEnabled( false );
2116 textToClipboard
->setText( i18n("Copy forbidden by DRM") );
2118 QAction
*choice
= menu
.exec( e
->globalPos() );
2119 // check if the user really selected an action
2122 if ( choice
== textToClipboard
)
2123 copyTextSelection();
2124 else if ( choice
== speakText
)
2126 const QString text
= d
->selectedText();
2127 d
->tts()->say( text
);
2134 // reset mouse press / 'drag start' position
2135 d
->mousePressPos
= QPoint();
2138 void PageView::wheelEvent( QWheelEvent
*e
)
2140 // don't perform any mouse action when viewport is autoscrolling
2141 if ( d
->viewportMoveActive
)
2144 if ( !d
->document
->isOpened() )
2146 QScrollArea::wheelEvent( e
);
2150 int delta
= e
->delta(),
2151 vScroll
= verticalScrollBar()->value();
2153 if ( (e
->modifiers() & Qt::ControlModifier
) == Qt::ControlModifier
) {
2154 if ( e
->delta() < 0 )
2159 else if ( delta
<= -120 && !Okular::Settings::viewContinuous() && vScroll
== verticalScrollBar()->maximum() )
2162 if ( (int)d
->document
->currentPage() < d
->items
.count() - 1 )
2164 // more optimized than document->setNextPage and then move view to top
2165 Okular::DocumentViewport newViewport
= d
->document
->viewport();
2166 newViewport
.pageNumber
+= d
->document
->currentPage() ? viewColumns() : 1;
2167 if ( newViewport
.pageNumber
>= (int)d
->items
.count() )
2168 newViewport
.pageNumber
= d
->items
.count() - 1;
2169 newViewport
.rePos
.enabled
= true;
2170 newViewport
.rePos
.normalizedY
= 0.0;
2171 d
->document
->setViewport( newViewport
);
2174 else if ( delta
>= 120 && !Okular::Settings::viewContinuous() && vScroll
== verticalScrollBar()->minimum() )
2177 if ( d
->document
->currentPage() > 0 )
2179 // more optimized than document->setPrevPage and then move view to bottom
2180 Okular::DocumentViewport newViewport
= d
->document
->viewport();
2181 newViewport
.pageNumber
-= viewColumns();
2182 if ( newViewport
.pageNumber
< 0 )
2183 newViewport
.pageNumber
= 0;
2184 newViewport
.rePos
.enabled
= true;
2185 newViewport
.rePos
.normalizedY
= 1.0;
2186 d
->document
->setViewport( newViewport
);
2190 QScrollArea::wheelEvent( e
);
2192 QPoint cp
= widget()->mapFromGlobal(mapToGlobal(e
->pos()));
2196 void PageView::dragEnterEvent( QDragEnterEvent
* ev
)
2201 void PageView::dragMoveEvent( QDragMoveEvent
* ev
)
2206 void PageView::dropEvent( QDropEvent
* ev
)
2208 if ( KUrl::List::canDecode( ev
->mimeData() ) )
2209 emit
urlDropped( KUrl::List::fromMimeData( ev
->mimeData() ).first() );
2213 QList
< Okular::RegularAreaRect
* > PageView::textSelections( const QPoint
& start
, const QPoint
& end
, int& firstpage
)
2216 QList
< Okular::RegularAreaRect
* > ret
;
2217 QSet
< int > affectedItemsSet
;
2218 QRect selectionRect
= QRect( start
, end
).normalized();
2219 foreach( PageViewItem
* item
, d
->items
)
2221 if ( item
->isVisible() && selectionRect
.intersects( item
->croppedGeometry() ) )
2222 affectedItemsSet
.insert( item
->pageNumber() );
2224 #ifdef PAGEVIEW_DEBUG
2225 kDebug() << ">>>> item selected by mouse:" << affectedItemsSet
.count();
2228 if ( !affectedItemsSet
.isEmpty() )
2230 // is the mouse drag line the ne-sw diagonal of the selection rect?
2231 bool direction_ne_sw
= start
== selectionRect
.topRight() || start
== selectionRect
.bottomLeft();
2233 int tmpmin
= d
->document
->pages();
2235 foreach( int p
, affectedItemsSet
)
2237 if ( p
< tmpmin
) tmpmin
= p
;
2238 if ( p
> tmpmax
) tmpmax
= p
;
2241 PageViewItem
* a
= pickItemOnPoint( (int)( direction_ne_sw
? selectionRect
.right() : selectionRect
.left() ), (int)selectionRect
.top() );
2242 int min
= a
&& ( a
->pageNumber() != tmpmax
) ? a
->pageNumber() : tmpmin
;
2243 PageViewItem
* b
= pickItemOnPoint( (int)( direction_ne_sw
? selectionRect
.left() : selectionRect
.right() ), (int)selectionRect
.bottom() );
2244 int max
= b
&& ( b
->pageNumber() != tmpmin
) ? b
->pageNumber() : tmpmax
;
2246 QList
< int > affectedItemsIds
;
2247 for ( int i
= min
; i
<= max
; ++i
)
2248 affectedItemsIds
.append( i
);
2249 #ifdef PAGEVIEW_DEBUG
2250 kDebug() << ">>>> pages:" << affectedItemsIds
;
2252 firstpage
= affectedItemsIds
.first();
2254 if ( affectedItemsIds
.count() == 1 )
2256 PageViewItem
* item
= d
->items
[ affectedItemsIds
.first() ];
2257 selectionRect
.translate( -item
->uncroppedGeometry().topLeft() );
2258 ret
.append( textSelectionForItem( item
,
2259 direction_ne_sw
? selectionRect
.topRight() : selectionRect
.topLeft(),
2260 direction_ne_sw
? selectionRect
.bottomLeft() : selectionRect
.bottomRight() ) );
2262 else if ( affectedItemsIds
.count() > 1 )
2265 PageViewItem
* first
= d
->items
[ affectedItemsIds
.first() ];
2266 QRect geom
= first
->croppedGeometry().intersect( selectionRect
).translated( -first
->uncroppedGeometry().topLeft() );
2267 ret
.append( textSelectionForItem( first
,
2268 selectionRect
.bottom() > geom
.height() ? ( direction_ne_sw
? geom
.topRight() : geom
.topLeft() ) : ( direction_ne_sw
? geom
.bottomRight() : geom
.bottomLeft() ),
2271 PageViewItem
* last
= d
->items
[ affectedItemsIds
.last() ];
2272 geom
= last
->croppedGeometry().intersect( selectionRect
).translated( -last
->uncroppedGeometry().topLeft() );
2273 // the last item needs to appended at last...
2274 Okular::RegularAreaRect
* lastArea
= textSelectionForItem( last
,
2276 selectionRect
.bottom() > geom
.height() ? ( direction_ne_sw
? geom
.bottomLeft() : geom
.bottomRight() ) : ( direction_ne_sw
? geom
.topLeft() : geom
.topRight() ) );
2277 affectedItemsIds
.removeFirst();
2278 affectedItemsIds
.removeLast();
2279 // item between the two above
2280 foreach( int page
, affectedItemsIds
)
2282 ret
.append( textSelectionForItem( d
->items
[ page
] ) );
2284 ret
.append( lastArea
);
2291 void PageView::drawDocumentOnPainter( const QRect
& contentsRect
, QPainter
* p
)
2293 // when checking if an Item is contained in contentsRect, instead of
2294 // growing PageViewItems rects (for keeping outline into account), we
2295 // grow the contentsRect
2296 QRect checkRect
= contentsRect
;
2297 checkRect
.adjust( -3, -3, 1, 1 );
2299 // create a region from which we'll subtract painted rects
2300 QRegion
remainingArea( contentsRect
);
2302 // iterate over all items painting the ones intersecting contentsRect
2303 QVector
< PageViewItem
* >::const_iterator iIt
= d
->items
.constBegin(), iEnd
= d
->items
.constEnd();
2304 for ( ; iIt
!= iEnd
; ++iIt
)
2306 // check if a piece of the page intersects the contents rect
2307 if ( !(*iIt
)->isVisible() || !(*iIt
)->croppedGeometry().intersects( checkRect
) )
2310 // get item and item's outline geometries
2311 PageViewItem
* item
= *iIt
;
2312 QRect itemGeometry
= item
->croppedGeometry(),
2313 outlineGeometry
= itemGeometry
;
2314 outlineGeometry
.adjust( -1, -1, 3, 3 );
2316 // move the painter to the top-left corner of the real page
2318 p
->translate( itemGeometry
.left(), itemGeometry
.top() );
2320 // draw the page outline (black border and 2px bottom-right shadow)
2321 if ( !itemGeometry
.contains( contentsRect
) )
2323 int itemWidth
= itemGeometry
.width(),
2324 itemHeight
= itemGeometry
.height();
2325 // draw simple outline
2326 p
->setPen( Qt::black
);
2327 p
->drawRect( -1, -1, itemWidth
+ 1, itemHeight
+ 1 );
2328 // draw bottom/right gradient
2329 static int levels
= 2;
2330 int r
= QColor(Qt::gray
).red() / (levels
+ 2),
2331 g
= QColor(Qt::gray
).green() / (levels
+ 2),
2332 b
= QColor(Qt::gray
).blue() / (levels
+ 2);
2333 for ( int i
= 0; i
< levels
; i
++ )
2335 p
->setPen( QColor( r
* (i
+2), g
* (i
+2), b
* (i
+2) ) );
2336 p
->drawLine( i
, i
+ itemHeight
+ 1, i
+ itemWidth
+ 1, i
+ itemHeight
+ 1 );
2337 p
->drawLine( i
+ itemWidth
+ 1, i
, i
+ itemWidth
+ 1, i
+ itemHeight
);
2338 p
->setPen( Qt::gray
);
2339 p
->drawLine( -1, i
+ itemHeight
+ 1, i
- 1, i
+ itemHeight
+ 1 );
2340 p
->drawLine( i
+ itemWidth
+ 1, -1, i
+ itemWidth
+ 1, i
- 1 );
2344 // draw the page using the PagePainter with all flags active
2345 if ( contentsRect
.intersects( itemGeometry
) )
2347 QRect pixmapRect
= contentsRect
.intersect( itemGeometry
);
2348 pixmapRect
.translate( -item
->croppedGeometry().topLeft() );
2349 PagePainter::paintCroppedPageOnPainter( p
, item
->page(), PAGEVIEW_ID
, pageflags
,
2350 item
->uncroppedWidth(), item
->uncroppedHeight(), pixmapRect
,
2354 // remove painted area from 'remainingArea' and restore painter
2355 remainingArea
-= outlineGeometry
.intersect( contentsRect
);
2359 // fill with background color the unpainted area
2360 const QVector
<QRect
> &backRects
= remainingArea
.rects();
2361 int backRectsNumber
= backRects
.count();
2362 // the previous color here was Qt::gray
2363 QColor backColor
= widget()->palette().color( QPalette::Dark
);
2364 for ( int jr
= 0; jr
< backRectsNumber
; jr
++ )
2365 p
->fillRect( backRects
[ jr
], backColor
);
2368 void PageView::updateItemSize( PageViewItem
* item
, int colWidth
, int rowHeight
)
2370 const Okular::Page
* okularPage
= item
->page();
2371 double width
= okularPage
->width(),
2372 height
= okularPage
->height(),
2373 zoom
= d
->zoomFactor
;
2374 Okular::NormalizedRect
crop( 0., 0., 1., 1. );
2377 if ( Okular::Settings::trimMargins() && okularPage
->isBoundingBoxKnown()
2378 && !okularPage
->boundingBox().isNull() )
2380 crop
= okularPage
->boundingBox();
2382 // Rotate the bounding box from upright Rotation0 to current page orientation:
2383 for ( int i
= okularPage
->totalOrientation(); i
> 0; --i
)
2385 Okular::NormalizedRect rot
= crop
;
2386 crop
.left
= 1 - rot
.bottom
;
2387 crop
.top
= rot
.left
;
2388 crop
.right
= 1 - rot
.top
;
2389 crop
.bottom
= rot
.right
;
2392 // Expand the crop slightly beyond the bounding box
2393 static const double cropExpandRatio
= 0.04;
2394 double cropExpand
= cropExpandRatio
* ( (crop
.right
-crop
.left
) + (crop
.bottom
-crop
.top
) ) / 2;
2395 crop
= Okular::NormalizedRect(
2396 crop
.left
- cropExpand
,
2397 crop
.top
- cropExpand
,
2398 crop
.right
+ cropExpand
,
2399 crop
.bottom
+ cropExpand
) & Okular::NormalizedRect( 0, 0, 1, 1 );
2401 // We currently generate a larger image and then crop it, so if the
2402 // crop rect is very small the generated image is huge. Hence, we shouldn't
2403 // let the crop rect become too small.
2404 // Make sure we crop by at most 50% in either dimension:
2405 static const double minCropRatio
= 0.5;
2406 if ( ( crop
.right
- crop
.left
) < minCropRatio
)
2408 double newLeft
= ( crop
.left
+ crop
.right
) / 2 - minCropRatio
/2;
2409 crop
.left
= qMax( 0.0, qMin( 1.0 - minCropRatio
, newLeft
) );
2410 crop
.right
= crop
.left
+ minCropRatio
;
2412 if ( ( crop
.bottom
- crop
.top
) < minCropRatio
)
2414 double newTop
= ( crop
.top
+ crop
.bottom
) / 2 - minCropRatio
/2;
2415 crop
.top
= qMax( 0.0, qMin( 1.0 - minCropRatio
, newTop
) );
2416 crop
.bottom
= crop
.top
+ minCropRatio
;
2419 width
*= ( crop
.right
- crop
.left
);
2420 height
*= ( crop
.bottom
- crop
.top
);
2421 #ifdef PAGEVIEW_DEBUG
2422 kDebug() << "Cropped page" << okularPage
->number() << "to" << crop
2423 << "width" << width
<< "height" << height
<< "by bbox" << okularPage
->boundingBox();
2427 if ( d
->zoomMode
== ZoomFixed
)
2431 item
->setWHZC( (int)width
, (int)height
, d
->zoomFactor
, crop
);
2433 else if ( d
->zoomMode
== ZoomFitWidth
)
2435 height
= ( height
/ width
) * colWidth
;
2436 zoom
= (double)colWidth
/ width
;
2437 item
->setWHZC( colWidth
, (int)height
, zoom
, crop
);
2438 d
->zoomFactor
= zoom
;
2440 else if ( d
->zoomMode
== ZoomFitPage
)
2442 double scaleW
= (double)colWidth
/ (double)width
;
2443 double scaleH
= (double)rowHeight
/ (double)height
;
2444 zoom
= qMin( scaleW
, scaleH
);
2445 item
->setWHZC( (int)(zoom
* width
), (int)(zoom
* height
), zoom
, crop
);
2446 d
->zoomFactor
= zoom
;
2450 kDebug() << "calling updateItemSize with unrecognized d->zoomMode!";
2454 PageViewItem
* PageView::pickItemOnPoint( int x
, int y
)
2456 PageViewItem
* item
= 0;
2457 QLinkedList
< PageViewItem
* >::const_iterator iIt
= d
->visibleItems
.constBegin(), iEnd
= d
->visibleItems
.constEnd();
2458 for ( ; iIt
!= iEnd
; ++iIt
)
2460 PageViewItem
* i
= *iIt
;
2461 const QRect
& r
= i
->croppedGeometry();
2462 if ( x
< r
.right() && x
> r
.left() && y
< r
.bottom() )
2472 void PageView::textSelectionClear()
2474 // something to clear
2475 if ( !d
->pagesWithTextSelection
.isEmpty() )
2477 QSet
< int >::ConstIterator it
= d
->pagesWithTextSelection
.constBegin(), itEnd
= d
->pagesWithTextSelection
.constEnd();
2478 for ( ; it
!= itEnd
; ++it
)
2479 d
->document
->setPageTextSelection( *it
, 0, QColor() );
2480 d
->pagesWithTextSelection
.clear();
2484 void PageView::selectionStart( const QPoint
& pos
, const QColor
& color
, bool /*aboveAll*/ )
2487 d
->mouseSelecting
= true;
2488 d
->mouseSelectionRect
.setRect( pos
.x(), pos
.y(), 1, 1 );
2489 d
->mouseSelectionColor
= color
;
2490 // ensures page doesn't scroll
2491 if ( d
->autoScrollTimer
)
2493 d
->scrollIncrement
= 0;
2494 d
->autoScrollTimer
->stop();
2498 void PageView::selectionEndPoint( const QPoint
& pos
)
2500 if ( !d
->mouseSelecting
)
2503 if (pos
.x() < horizontalScrollBar()->value()) d
->dragScrollVector
.setX(pos
.x() - horizontalScrollBar()->value());
2504 else if (horizontalScrollBar()->value() + viewport()->width() < pos
.x()) d
->dragScrollVector
.setX(pos
.x() - horizontalScrollBar()->value() - viewport()->width());
2505 else d
->dragScrollVector
.setX(0);
2507 if (pos
.y() < verticalScrollBar()->value()) d
->dragScrollVector
.setY(pos
.y() - verticalScrollBar()->value());
2508 else if (verticalScrollBar()->value() + viewport()->height() < pos
.y()) d
->dragScrollVector
.setY(pos
.y() - verticalScrollBar()->value() - viewport()->height());
2509 else d
->dragScrollVector
.setY(0);
2511 if (d
->dragScrollVector
!= QPoint(0, 0))
2513 if (!d
->dragScrollTimer
.isActive()) d
->dragScrollTimer
.start(100);
2515 else d
->dragScrollTimer
.stop();
2517 // update the selection rect
2518 QRect updateRect
= d
->mouseSelectionRect
;
2519 d
->mouseSelectionRect
.setBottomLeft( pos
);
2520 updateRect
|= d
->mouseSelectionRect
;
2521 widget()->update( updateRect
.adjusted( -1, -1, 1, 1 ) );
2524 static Okular::NormalizedPoint
rotateInNormRect( const QPoint
&rotated
, const QRect
&rect
, Okular::Rotation rotation
)
2526 Okular::NormalizedPoint ret
;
2530 case Okular::Rotation0
:
2531 ret
= Okular::NormalizedPoint( rotated
.x(), rotated
.y(), rect
.width(), rect
.height() );
2533 case Okular::Rotation90
:
2534 ret
= Okular::NormalizedPoint( rotated
.y(), rect
.width() - rotated
.x(), rect
.height(), rect
.width() );
2536 case Okular::Rotation180
:
2537 ret
= Okular::NormalizedPoint( rect
.width() - rotated
.x(), rect
.height() - rotated
.y(), rect
.width(), rect
.height() );
2539 case Okular::Rotation270
:
2540 ret
= Okular::NormalizedPoint( rect
.height() - rotated
.y(), rotated
.x(), rect
.height(), rect
.width() );
2547 Okular::RegularAreaRect
* PageView::textSelectionForItem( PageViewItem
* item
, const QPoint
& startPoint
, const QPoint
& endPoint
)
2549 const QRect
& geometry
= item
->uncroppedGeometry();
2550 Okular::NormalizedPoint
startCursor( 0.0, 0.0 );
2551 if ( !startPoint
.isNull() )
2553 startCursor
= rotateInNormRect( startPoint
, geometry
, item
->page()->rotation() );
2555 Okular::NormalizedPoint
endCursor( 1.0, 1.0 );
2556 if ( !endPoint
.isNull() )
2558 endCursor
= rotateInNormRect( endPoint
, geometry
, item
->page()->rotation() );
2560 Okular::TextSelection
mouseTextSelectionInfo( startCursor
, endCursor
);
2562 const Okular::Page
* okularPage
= item
->page();
2564 if ( !okularPage
->hasTextPage() )
2565 d
->document
->requestTextPage( okularPage
->number() );
2567 Okular::RegularAreaRect
* selectionArea
= okularPage
->textArea( &mouseTextSelectionInfo
);
2568 #ifdef PAGEVIEW_DEBUG
2569 kDebug().nospace() << "text areas (" << okularPage
->number() << "): " << ( selectionArea
? QString::number( selectionArea
->count() ) : "(none)" );
2571 return selectionArea
;
2574 void PageView::selectionClear()
2576 QRect updatedRect
= d
->mouseSelectionRect
.normalized().adjusted( 0, 0, 1, 1 );
2577 d
->mouseSelecting
= false;
2578 d
->mouseSelectionRect
.setCoords( 0, 0, 0, 0 );
2579 widget()->update( updatedRect
);
2582 void PageView::updateZoom( ZoomMode newZoomMode
)
2584 if ( newZoomMode
== ZoomFixed
)
2586 if ( d
->aZoom
->currentItem() == 0 )
2587 newZoomMode
= ZoomFitWidth
;
2588 else if ( d
->aZoom
->currentItem() == 1 )
2589 newZoomMode
= ZoomFitPage
;
2592 float newFactor
= d
->zoomFactor
;
2593 QAction
* checkedZoomAction
= 0;
2594 switch ( newZoomMode
)
2596 case ZoomFixed
:{ //ZoomFixed case
2597 QString z
= d
->aZoom
->currentText();
2598 // kdelibs4 sometimes adds accelerators to actions' text directly :(
2601 newFactor
= KGlobal::locale()->readNumber( z
) / 100.0;
2604 newFactor
+= (newFactor
> 0.99) ? ( newFactor
> 1.99 ? 0.5 : 0.2 ) : 0.1;
2605 newZoomMode
= ZoomFixed
;
2608 newFactor
-= (newFactor
> 1.01) ? ( newFactor
> 2.01 ? 0.5 : 0.2 ) : 0.1;
2609 newZoomMode
= ZoomFixed
;
2612 checkedZoomAction
= d
->aZoomFitWidth
;
2615 checkedZoomAction
= d
->aZoomFitPage
;
2618 checkedZoomAction
= d
->aZoomFitText
;
2620 case ZoomRefreshCurrent
:
2621 newZoomMode
= ZoomFixed
;
2625 if ( newFactor
> 4.0 )
2627 if ( newFactor
< 0.1 )
2630 if ( newZoomMode
!= d
->zoomMode
|| (newZoomMode
== ZoomFixed
&& newFactor
!= d
->zoomFactor
) )
2632 // rebuild layout and update the whole viewport
2633 d
->zoomMode
= newZoomMode
;
2634 d
->zoomFactor
= newFactor
;
2635 // be sure to block updates to document's viewport
2636 bool prevState
= d
->blockViewport
;
2637 d
->blockViewport
= true;
2638 slotRelayoutPages();
2639 d
->blockViewport
= prevState
;
2641 slotRequestVisiblePixmaps();
2644 // update actions checked state
2645 if ( d
->aZoomFitWidth
)
2647 d
->aZoomFitWidth
->setChecked( checkedZoomAction
== d
->aZoomFitWidth
);
2648 d
->aZoomFitPage
->setChecked( checkedZoomAction
== d
->aZoomFitPage
);
2649 // d->aZoomFitText->setChecked( checkedZoomAction == d->aZoomFitText );
2653 d
->aZoomIn
->setEnabled( d
->zoomFactor
< 3.9 );
2654 d
->aZoomOut
->setEnabled( d
->zoomFactor
> 0.2 );
2657 void PageView::updateZoomText()
2659 // use current page zoom as zoomFactor if in ZoomFit/* mode
2660 if ( d
->zoomMode
!= ZoomFixed
&& d
->items
.count() > 0 )
2661 d
->zoomFactor
= d
->items
[ qMax( 0, (int)d
->document
->currentPage() ) ]->zoomFactor();
2662 float newFactor
= d
->zoomFactor
;
2663 d
->aZoom
->removeAllActions();
2665 // add items that describe fit actions
2666 QStringList translated
;
2667 translated
<< i18n("Fit Width") << i18n("Fit Page") /*<< i18n("Fit Text")*/;
2669 // add percent items
2670 QString
double_oh( "00" );
2671 const float zoomValue
[10] = { 0.12, 0.25, 0.33, 0.50, 0.66, 0.75, 1.00, 1.25, 1.50, 2.00 };
2673 selIdx
= 2; // use 3 if "fit text" present
2674 bool inserted
= false; //use: "d->zoomMode != ZoomFixed" to hide Fit/* zoom ratio
2675 while ( idx
< 10 || !inserted
)
2677 float value
= idx
< 10 ? zoomValue
[ idx
] : newFactor
;
2678 if ( !inserted
&& newFactor
< (value
- 0.0001) )
2682 if ( value
> (newFactor
- 0.0001) && value
< (newFactor
+ 0.0001) )
2686 QString
localValue( KGlobal::locale()->formatNumber( value
* 100.0, 2 ) );
2687 localValue
.remove( KGlobal::locale()->decimalSymbol() + double_oh
);
2688 // remove a trailing zero in numbers like 66.70
2689 if ( localValue
.right( 1 ) == QLatin1String( "0" ) && localValue
.indexOf( KGlobal::locale()->decimalSymbol() ) > -1 )
2690 localValue
.chop( 1 );
2691 translated
<< QString( "%1%" ).arg( localValue
);
2693 d
->aZoom
->setItems( translated
);
2695 // select current item in list
2696 if ( d
->zoomMode
== ZoomFitWidth
)
2698 else if ( d
->zoomMode
== ZoomFitPage
)
2700 else if ( d
->zoomMode
== ZoomFitText
)
2702 d
->aZoom
->setCurrentItem( selIdx
);
2705 void PageView::updateCursor( const QPoint
&p
)
2707 // detect the underlaying page (if present)
2708 PageViewItem
* pageItem
= pickItemOnPoint( p
.x(), p
.y() );
2711 double nX
= pageItem
->absToPageX(p
.x());
2712 double nY
= pageItem
->absToPageY(p
.y());
2714 // if over a ObjectRect (of type Link) change cursor to hand
2715 if ( d
->mouseMode
== MouseTextSelect
)
2716 setCursor( Qt::IBeamCursor
);
2717 else if ( d
->mouseAnn
)
2718 setCursor( Qt::ClosedHandCursor
);
2721 const Okular::ObjectRect
* linkobj
= pageItem
->page()->objectRect( Okular::ObjectRect::Action
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
2722 const Okular::ObjectRect
* annotobj
= pageItem
->page()->objectRect( Okular::ObjectRect::OAnnotation
, nX
, nY
, pageItem
->uncroppedWidth(), pageItem
->uncroppedHeight() );
2723 if ( linkobj
&& !annotobj
)
2725 d
->mouseOnRect
= true;
2726 setCursor( Qt::PointingHandCursor
);
2730 d
->mouseOnRect
= false;
2732 && ( QApplication::keyboardModifiers() & Qt::ControlModifier
)
2733 && static_cast< const Okular::AnnotationObjectRect
* >( annotobj
)->annotation()->canBeMoved() )
2735 setCursor( Qt::OpenHandCursor
);
2739 setCursor( Qt::ArrowCursor
);
2746 // if there's no page over the cursor and we were showing the pointingHandCursor
2747 // go back to the normal one
2748 d
->mouseOnRect
= false;
2749 setCursor( Qt::ArrowCursor
);
2753 int PageView::viewColumns() const
2755 int nr
= Okular::Settings::viewMode();
2758 return Okular::Settings::viewColumns();
2761 int PageView::viewRows() const
2763 if ( Okular::Settings::viewMode() < 2 )
2765 return Okular::Settings::viewRows();
2768 void PageView::center(int cx
, int cy
)
2770 horizontalScrollBar()->setValue(cx
- viewport()->width() / 2);
2771 verticalScrollBar()->setValue(cy
- viewport()->height() / 2);
2774 void PageView::toggleFormWidgets( bool on
)
2776 bool somehadfocus
= false;
2777 QVector
< PageViewItem
* >::const_iterator dIt
= d
->items
.constBegin(), dEnd
= d
->items
.constEnd();
2778 for ( ; dIt
!= dEnd
; ++dIt
)
2780 bool hadfocus
= (*dIt
)->setFormWidgetsVisible( on
);
2781 somehadfocus
= somehadfocus
|| hadfocus
;
2785 d
->m_formsVisible
= on
;
2786 if ( d
->aToggleForms
) // it may not exist if we are on dummy mode
2788 if ( d
->m_formsVisible
)
2790 d
->aToggleForms
->setText( i18n( "Hide Forms" ) );
2794 d
->aToggleForms
->setText( i18n( "Show Forms" ) );
2799 //BEGIN private SLOTS
2800 void PageView::slotRelayoutPages()
2801 // called by: notifySetup, viewportResizeEvent, slotViewMode, slotContinuousToggled, updateZoom
2803 // set an empty container if we have no pages
2804 int pageCount
= d
->items
.count();
2805 if ( pageCount
< 1 )
2807 setWidgetResizable(true);
2811 // if viewport was auto-moving, stop it
2812 if ( d
->viewportMoveActive
)
2814 center( d
->viewportMoveDest
.x(), d
->viewportMoveDest
.y() );
2815 d
->viewportMoveActive
= false;
2816 d
->viewportMoveTimer
->stop();
2817 verticalScrollBar()->setEnabled( true );
2818 horizontalScrollBar()->setEnabled( true );
2821 // common iterator used in this method and viewport parameters
2822 QVector
< PageViewItem
* >::const_iterator iIt
, iEnd
= d
->items
.constEnd();
2823 int viewportWidth
= viewport()->width(),
2824 viewportHeight
= viewport()->height(),
2827 QRect
viewportRect( horizontalScrollBar()->value(), verticalScrollBar()->value(), viewportWidth
, viewportHeight
);
2829 // handle the 'center first page in row' stuff
2830 int nCols
= viewColumns();
2831 bool centerFirstPage
= Okular::Settings::centerFirstPageInRow() && nCols
> 1;
2832 const bool continuousView
= Okular::Settings::viewContinuous();
2834 // set all items geometry and resize contents. handle 'continuous' and 'single' modes separately
2836 PageViewItem
* currentItem
= d
->items
[ qMax( 0, (int)d
->document
->currentPage() ) ];
2838 // handle the 'centering on first row' stuff
2839 if ( centerFirstPage
)
2840 pageCount
+= nCols
- 1;
2841 // Here we find out column's width and row's height to compute a table
2842 // so we can place widgets 'centered in virtual cells'.
2845 // if ( Okular::Settings::viewMode() < 2 )
2846 nRows
= (int)ceil( (float)pageCount
/ (float)nCols
);
2847 // nRows=(int)ceil( (float)pageCount / (float) Okular::Settings::viewRows() );
2849 // nRows = Okular::Settings::viewRows();
2851 int * colWidth
= new int[ nCols
],
2852 * rowHeight
= new int[ nRows
],
2855 for ( int i
= 0; i
< nCols
; i
++ )
2856 colWidth
[ i
] = viewportWidth
/ nCols
;
2857 for ( int i
= 0; i
< nRows
; i
++ )
2859 // handle the 'centering on first row' stuff
2860 if ( centerFirstPage
)
2862 pageCount
-= nCols
- 1;
2866 // 1) find the maximum columns width and rows height for a grid in
2867 // which each page must well-fit inside a cell
2868 for ( iIt
= d
->items
.constBegin(); iIt
!= iEnd
; ++iIt
)
2870 PageViewItem
* item
= *iIt
;
2871 // update internal page size (leaving a little margin in case of Fit* modes)
2872 updateItemSize( item
, colWidth
[ cIdx
] - 6, viewportHeight
- 12 );
2873 // find row's maximum height and column's max width
2874 if ( item
->croppedWidth() + 6 > colWidth
[ cIdx
] )
2875 colWidth
[ cIdx
] = item
->croppedWidth() + 6;
2876 if ( item
->croppedHeight() + 12 > rowHeight
[ rIdx
] )
2877 rowHeight
[ rIdx
] = item
->croppedHeight() + 12;
2878 // handle the 'centering on first row' stuff
2879 // update col/row indices
2880 if ( ++cIdx
== nCols
)
2887 const int pageRowIdx
= ( ( centerFirstPage
? nCols
- 1 : 0 ) + currentItem
->pageNumber() ) / nCols
;
2889 // 2) compute full size
2890 for ( int i
= 0; i
< nCols
; i
++ )
2891 fullWidth
+= colWidth
[ i
];
2892 if ( continuousView
)
2894 for ( int i
= 0; i
< nRows
; i
++ )
2895 fullHeight
+= rowHeight
[ i
];
2898 fullHeight
= rowHeight
[ pageRowIdx
];
2900 // 3) arrange widgets inside cells (and refine fullHeight if needed)
2902 insertY
= fullHeight
< viewportHeight
? ( viewportHeight
- fullHeight
) / 2 : 0;
2903 const int origInsertY
= insertY
;
2906 if ( centerFirstPage
)
2909 for ( int i
= 0; i
< cIdx
; ++i
)
2910 insertX
+= colWidth
[ i
];
2912 for ( iIt
= d
->items
.constBegin(); iIt
!= iEnd
; ++iIt
)
2914 PageViewItem
* item
= *iIt
;
2915 int cWidth
= colWidth
[ cIdx
],
2916 rHeight
= rowHeight
[ rIdx
];
2917 if ( continuousView
|| rIdx
== pageRowIdx
)
2919 const bool reallyDoCenterFirst
= item
->pageNumber() == 0 && centerFirstPage
;
2920 item
->moveTo( (reallyDoCenterFirst
? 0 : insertX
) + ( (reallyDoCenterFirst
? fullWidth
: cWidth
) - item
->croppedWidth()) / 2,
2921 (continuousView
? insertY
: origInsertY
) + (rHeight
- item
->croppedHeight()) / 2 );
2922 item
->setVisible( true );
2926 item
->moveTo( 0, 0 );
2927 item
->setVisible( false );
2929 item
->setFormWidgetsVisible( d
->m_formsVisible
);
2930 // advance col/row index
2932 if ( ++cIdx
== nCols
)
2939 #ifdef PAGEVIEW_DEBUG
2940 kWarning() << "updating size for pageno" << item
->pageNumber() << "cropped" << item
->croppedGeometry() << "uncropped" << item
->uncroppedGeometry();
2945 delete [] rowHeight
;
2947 // 3) reset dirty state
2948 d
->dirtyLayout
= false;
2950 horizontalScrollBar()->setRange( 0, qMax( 0, fullWidth
- viewport()->width() ) );
2951 verticalScrollBar()->setRange( 0, qMax( 0, fullHeight
- viewport()->height() ) );
2953 // 4) update scrollview's contents size and recenter view
2954 bool wasUpdatesEnabled
= viewport()->updatesEnabled();
2955 if ( fullWidth
!= widget()->width() || fullHeight
!= widget()->height() )
2957 // disable updates and resize the viewportContents
2958 if ( wasUpdatesEnabled
)
2959 viewport()->setUpdatesEnabled( false );
2960 setWidgetResizable(false);
2961 fullWidth
= qMax(fullWidth
, viewport()->width());
2962 fullHeight
= qMax(fullHeight
, viewport()->height());
2963 widget()->resize( fullWidth
, fullHeight
);
2964 // restore previous viewport if defined and updates enabled
2965 if ( wasUpdatesEnabled
)
2967 const Okular::DocumentViewport
& vp
= d
->document
->viewport();
2968 if ( vp
.pageNumber
>= 0 )
2970 int prevX
= horizontalScrollBar()->value(),
2971 prevY
= verticalScrollBar()->value();
2972 const QRect
& geometry
= d
->items
[ vp
.pageNumber
]->croppedGeometry();
2973 double nX
= vp
.rePos
.enabled
? vp
.rePos
.normalizedX
: 0.5,
2974 nY
= vp
.rePos
.enabled
? vp
.rePos
.normalizedY
: 0.0;
2975 center( geometry
.left() + qRound( nX
* (double)geometry
.width() ),
2976 geometry
.top() + qRound( nY
* (double)geometry
.height() ) );
2977 // center() usually moves the viewport, that requests pixmaps too.
2978 // if that doesn't happen we have to request them by hand
2979 if ( prevX
== horizontalScrollBar()->value() && prevY
== verticalScrollBar()->value() )
2980 slotRequestVisiblePixmaps();
2982 // or else go to center page
2984 center( fullWidth
/ 2, 0 );
2985 viewport()->setUpdatesEnabled( true );
2989 // 5) update the whole viewport if updated enabled
2990 if ( wasUpdatesEnabled
)
2994 void PageView::slotRequestVisiblePixmaps( int newValue
)
2996 // if requests are blocked (because raised by an unwanted event), exit
2997 if ( d
->blockPixmapsRequest
|| d
->viewportMoveActive
||
2998 d
->mouseMidZooming
)
3001 // precalc view limits for intersecting with page coords inside the lOOp
3002 bool isEvent
= newValue
!= -1 && !d
->blockViewport
;
3003 QRect
viewportRect( horizontalScrollBar()->value(),
3004 verticalScrollBar()->value(),
3005 viewport()->width(), viewport()->height() );
3007 // some variables used to determine the viewport
3008 int nearPageNumber
= -1;
3009 double viewportCenterX
= (viewportRect
.left() + viewportRect
.right()) / 2.0,
3010 viewportCenterY
= (viewportRect
.top() + viewportRect
.bottom()) / 2.0,
3015 // iterate over all items
3016 d
->visibleItems
.clear();
3017 QLinkedList
< Okular::PixmapRequest
* > requestedPixmaps
;
3018 QVector
< Okular::VisiblePageRect
* > visibleRects
;
3019 QVector
< PageViewItem
* >::const_iterator iIt
= d
->items
.constBegin(), iEnd
= d
->items
.constEnd();
3020 for ( ; iIt
!= iEnd
; ++iIt
)
3022 PageViewItem
* i
= *iIt
;
3023 if ( !i
->isVisible() )
3025 #ifdef PAGEVIEW_DEBUG
3026 kWarning() << "checking page" << i
->pageNumber();
3027 kWarning().nospace() << "viewportRect is " << viewportRect
<< ", page item is " << i
->croppedGeometry() << " intersect : " << viewportRect
.intersects( i
->croppedGeometry() );
3029 // if the item doesn't intersect the viewport, skip it
3030 QRect intersectionRect
= viewportRect
.intersect( i
->croppedGeometry() );
3031 if ( intersectionRect
.isEmpty() )
3034 // add the item to the 'visible list'
3035 d
->visibleItems
.push_back( i
);
3036 Okular::VisiblePageRect
* vItem
= new Okular::VisiblePageRect( i
->pageNumber(), Okular::NormalizedRect( intersectionRect
.translated( -i
->uncroppedGeometry().topLeft() ), i
->uncroppedWidth(), i
->uncroppedHeight() ) );
3037 visibleRects
.push_back( vItem
);
3038 #ifdef PAGEVIEW_DEBUG
3039 kWarning() << "checking for pixmap for page" << i
->pageNumber() << "=" << i
->page()->hasPixmap( PAGEVIEW_ID
, i
->uncroppedWidth(), i
->uncroppedHeight() );
3040 kWarning() << "checking for text for page" << i
->pageNumber() << "=" << i
->page()->hasTextPage();
3042 // if the item has not the right pixmap, add a request for it
3043 // TODO: We presently request a pixmap for the full page, and then render just the crop part. This waste memory and cycles.
3044 if ( !i
->page()->hasPixmap( PAGEVIEW_ID
, i
->uncroppedWidth(), i
->uncroppedHeight() ) )
3046 #ifdef PAGEVIEW_DEBUG
3047 kWarning() << "rerequesting visible pixmaps for page" << i
->pageNumber() << "!";
3049 Okular::PixmapRequest
* p
= new Okular::PixmapRequest(
3050 PAGEVIEW_ID
, i
->pageNumber(), i
->uncroppedWidth(), i
->uncroppedHeight(), PAGEVIEW_PRIO
, true );
3051 requestedPixmaps
.push_back( p
);
3054 // look for the item closest to viewport center and the relative
3055 // position between the item and the viewport center
3058 const QRect
& geometry
= i
->croppedGeometry();
3059 // compute distance between item center and viewport center (slightly moved left)
3060 double distance
= hypot( (geometry
.left() + geometry
.right()) / 2 - (viewportCenterX
- 4),
3061 (geometry
.top() + geometry
.bottom()) / 2 - viewportCenterY
);
3062 if ( distance
>= minDistance
&& nearPageNumber
!= -1 )
3064 nearPageNumber
= i
->pageNumber();
3065 minDistance
= distance
;
3066 if ( geometry
.height() > 0 && geometry
.width() > 0 )
3068 focusedX
= ( viewportCenterX
- (double)geometry
.left() ) / (double)geometry
.width();
3069 focusedY
= ( viewportCenterY
- (double)geometry
.top() ) / (double)geometry
.height();
3074 // if preloading is enabled, add the pages before and after in preloading
3075 if ( !d
->visibleItems
.isEmpty() &&
3076 Okular::Settings::memoryLevel() != Okular::Settings::EnumMemoryLevel::Low
&&
3077 Okular::Settings::enableThreading() )
3079 // as the requests are done in the order as they appear in the list,
3080 // request first the next page and then the previous
3082 // add the page after the 'visible series' in preload
3083 int tailRequest
= d
->visibleItems
.last()->pageNumber() + 1;
3084 if ( tailRequest
< (int)d
->items
.count() )
3086 PageViewItem
* i
= d
->items
[ tailRequest
];
3087 // request the pixmap if not already present
3088 if ( !i
->page()->hasPixmap( PAGEVIEW_ID
, i
->uncroppedWidth(), i
->uncroppedHeight() ) && i
->uncroppedWidth() > 0 )
3089 requestedPixmaps
.push_back( new Okular::PixmapRequest(
3090 PAGEVIEW_ID
, i
->pageNumber(), i
->uncroppedWidth(), i
->uncroppedHeight(), PAGEVIEW_PRELOAD_PRIO
, true ) );
3093 // add the page before the 'visible series' in preload
3094 int headRequest
= d
->visibleItems
.first()->pageNumber() - 1;
3095 if ( headRequest
>= 0 )
3097 PageViewItem
* i
= d
->items
[ headRequest
];
3098 // request the pixmap if not already present
3099 if ( !i
->page()->hasPixmap( PAGEVIEW_ID
, i
->uncroppedWidth(), i
->uncroppedHeight() ) && i
->uncroppedWidth() > 0 )
3100 requestedPixmaps
.push_back( new Okular::PixmapRequest(
3101 PAGEVIEW_ID
, i
->pageNumber(), i
->uncroppedWidth(), i
->uncroppedHeight(), PAGEVIEW_PRELOAD_PRIO
, true ) );
3105 // send requests to the document
3106 if ( !requestedPixmaps
.isEmpty() )
3108 d
->document
->requestPixmaps( requestedPixmaps
);
3110 // if this functions was invoked by viewport events, send update to document
3111 if ( isEvent
&& nearPageNumber
!= -1 )
3113 // determine the document viewport
3114 Okular::DocumentViewport
newViewport( nearPageNumber
);
3115 newViewport
.rePos
.enabled
= true;
3116 newViewport
.rePos
.normalizedX
= focusedX
;
3117 newViewport
.rePos
.normalizedY
= focusedY
;
3118 // set the viewport to other observers
3119 d
->document
->setViewport( newViewport
, PAGEVIEW_ID
);
3121 d
->document
->setVisiblePageRects( visibleRects
, PAGEVIEW_ID
);
3124 void PageView::slotMoveViewport()
3126 // converge to viewportMoveDest in 1 second
3127 int diffTime
= d
->viewportMoveTime
.elapsed();
3128 if ( diffTime
>= 667 || !d
->viewportMoveActive
)
3130 center( d
->viewportMoveDest
.x(), d
->viewportMoveDest
.y() );
3131 d
->viewportMoveTimer
->stop();
3132 d
->viewportMoveActive
= false;
3133 slotRequestVisiblePixmaps();
3134 verticalScrollBar()->setEnabled( true );
3135 horizontalScrollBar()->setEnabled( true );
3139 // move the viewport smoothly (kmplot: p(x)=1+0.47*(x-1)^3-0.25*(x-1)^4)
3140 float convergeSpeed
= (float)diffTime
/ 667.0,
3141 x
= ((float)viewport()->width() / 2.0) + horizontalScrollBar()->value(),
3142 y
= ((float)viewport()->height() / 2.0) + verticalScrollBar()->value(),
3143 diffX
= (float)d
->viewportMoveDest
.x() - x
,
3144 diffY
= (float)d
->viewportMoveDest
.y() - y
;
3145 convergeSpeed
*= convergeSpeed
* (1.4 - convergeSpeed
);
3146 center( (int)(x
+ diffX
* convergeSpeed
),
3147 (int)(y
+ diffY
* convergeSpeed
) );
3150 void PageView::slotAutoScoll()
3152 // the first time create the timer
3153 if ( !d
->autoScrollTimer
)
3155 d
->autoScrollTimer
= new QTimer( this );
3156 d
->autoScrollTimer
->setSingleShot( true );
3157 connect( d
->autoScrollTimer
, SIGNAL( timeout() ), this, SLOT( slotAutoScoll() ) );
3160 // if scrollIncrement is zero, stop the timer
3161 if ( !d
->scrollIncrement
)
3163 d
->autoScrollTimer
->stop();
3167 // compute delay between timer ticks and scroll amount per tick
3168 int index
= abs( d
->scrollIncrement
) - 1; // 0..9
3169 const int scrollDelay
[10] = { 200, 100, 50, 30, 20, 30, 25, 20, 30, 20 };
3170 const int scrollOffset
[10] = { 1, 1, 1, 1, 1, 2, 2, 2, 4, 4 };
3171 d
->autoScrollTimer
->start( scrollDelay
[ index
] );
3172 int delta
= d
->scrollIncrement
> 0 ? scrollOffset
[ index
] : -scrollOffset
[ index
];
3173 verticalScrollBar()->setValue(verticalScrollBar()->value() + delta
);
3176 void PageView::slotDragScroll()
3178 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + d
->dragScrollVector
.x());
3179 verticalScrollBar()->setValue(verticalScrollBar()->value() + d
->dragScrollVector
.y());
3180 QPoint p
= widget()->mapFromGlobal( QCursor::pos() );
3181 selectionEndPoint( p
);
3184 void PageView::slotShowWelcome()
3186 // show initial welcome text
3187 d
->messageWindow
->display( i18n( "Welcome" ), PageViewMessage::Info
, 2000 );
3190 void PageView::slotZoom()
3193 updateZoom( ZoomFixed
);
3196 void PageView::slotZoomIn()
3198 updateZoom( ZoomIn
);
3201 void PageView::slotZoomOut()
3203 updateZoom( ZoomOut
);
3206 void PageView::slotFitToWidthToggled( bool on
)
3208 if ( on
) updateZoom( ZoomFitWidth
);
3211 void PageView::slotFitToPageToggled( bool on
)
3213 if ( on
) updateZoom( ZoomFitPage
);
3216 void PageView::slotFitToTextToggled( bool on
)
3218 if ( on
) updateZoom( ZoomFitText
);
3221 void PageView::slotViewMode( QAction
*action
)
3223 const int nr
= action
->data().toInt();
3224 if ( (int)Okular::Settings::viewMode() != nr
)
3226 Okular::Settings::setViewMode( nr
);
3227 Okular::Settings::self()->writeConfig();
3228 if ( d
->document
->pages() > 0 )
3229 slotRelayoutPages();
3233 void PageView::slotContinuousToggled( bool on
)
3235 if ( Okular::Settings::viewContinuous() != on
)
3237 Okular::Settings::setViewContinuous( on
);
3238 Okular::Settings::self()->writeConfig();
3239 if ( d
->document
->pages() > 0 )
3240 slotRelayoutPages();
3244 void PageView::slotSetMouseNormal()
3246 d
->mouseMode
= MouseNormal
;
3247 // hide the messageWindow
3248 d
->messageWindow
->hide();
3249 // reshow the annotator toolbar if hiding was forced
3250 if ( d
->aToggleAnnotator
->isChecked() )
3251 slotToggleAnnotator( true );
3252 // force an update of the cursor
3253 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
3256 void PageView::slotSetMouseZoom()
3258 d
->mouseMode
= MouseZoom
;
3259 // change the text in messageWindow (and show it if hidden)
3260 d
->messageWindow
->display( i18n( "Select zooming area. Right-click to zoom out." ), 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::slotSetMouseSelect()
3270 d
->mouseMode
= MouseSelect
;
3271 // change the text in messageWindow (and show it if hidden)
3272 d
->messageWindow
->display( i18n( "Draw a rectangle around the text/graphics to copy." ), PageViewMessage::Info
, -1 );
3273 // force hiding of annotator toolbar
3275 d
->annotator
->setEnabled( false );
3276 // force an update of the cursor
3277 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
3280 void PageView::slotSetMouseTextSelect()
3282 d
->mouseMode
= MouseTextSelect
;
3283 // change the text in messageWindow (and show it if hidden)
3284 d
->messageWindow
->display( i18n( "Select text" ), PageViewMessage::Info
, -1 );
3285 // force hiding of annotator toolbar
3287 d
->annotator
->setEnabled( false );
3288 // force an update of the cursor
3289 updateCursor( widget()->mapFromGlobal( QCursor::pos() ) );
3292 void PageView::slotToggleAnnotator( bool on
)
3294 // the 'inHere' trick is needed as the slotSetMouseZoom() calls this
3295 static bool inHere
= false;
3300 // the annotator can be used in normal mouse mode only, so if asked for it,
3301 // switch to normal mode
3302 if ( on
&& d
->mouseMode
!= MouseNormal
)
3303 d
->aMouseNormal
->trigger();
3305 // create the annotator object if not present
3306 if ( !d
->annotator
)
3308 d
->annotator
= new PageViewAnnotator( this, d
->document
);
3309 bool allowTools
= d
->document
->pages() > 0 && d
->document
->isAllowed( Okular::AllowNotes
);
3310 d
->annotator
->setToolsEnabled( allowTools
);
3311 d
->annotator
->setTextToolsEnabled( allowTools
&& d
->document
->supportsSearching() );
3314 // initialize/reset annotator (and show/hide toolbar)
3315 d
->annotator
->setEnabled( on
);
3320 void PageView::slotScrollUp()
3322 if ( d
->scrollIncrement
< -9 )
3324 d
->scrollIncrement
--;
3329 void PageView::slotScrollDown()
3331 if ( d
->scrollIncrement
> 9 )
3333 d
->scrollIncrement
++;
3338 void PageView::slotRotateClockwise()
3340 int id
= ( (int)d
->document
->rotation() + 1 ) % 4;
3341 d
->document
->setRotation( id
);
3344 void PageView::slotRotateCounterClockwise()
3346 int id
= ( (int)d
->document
->rotation() + 3 ) % 4;
3347 d
->document
->setRotation( id
);
3350 void PageView::slotRotateOriginal()
3352 d
->document
->setRotation( 0 );
3355 void PageView::slotPageSizes( int newsize
)
3357 if ( newsize
< 0 || newsize
>= d
->document
->pageSizes().count() )
3360 d
->document
->setPageSize( d
->document
->pageSizes().at( newsize
) );
3363 void PageView::slotTrimMarginsToggled( bool on
)
3365 if ( Okular::Settings::trimMargins() != on
)
3367 Okular::Settings::setTrimMargins( on
);
3368 Okular::Settings::self()->writeConfig();
3369 if ( d
->document
->pages() > 0 )
3371 slotRelayoutPages();
3372 slotRequestVisiblePixmaps(); // TODO: slotRelayoutPages() may have done this already!
3377 void PageView::slotToggleForms()
3379 toggleFormWidgets( !d
->m_formsVisible
);
3382 void PageView::slotFormWidgetChanged( FormWidgetIface
*w
)
3384 if ( !d
->refreshTimer
)
3386 d
->refreshTimer
= new QTimer( this );
3387 d
->refreshTimer
->setSingleShot( true );
3388 connect( d
->refreshTimer
, SIGNAL( timeout() ),
3389 this, SLOT( slotRefreshPage() ) );
3391 d
->refreshPage
= w
->pageItem()->pageNumber();
3392 d
->refreshTimer
->start( 1000 );
3395 void PageView::slotRefreshPage()
3397 const int req
= d
->refreshPage
;
3400 d
->refreshPage
= -1;
3401 QMetaObject::invokeMethod( d
->document
, "refreshPixmaps", Qt::QueuedConnection
,
3402 Q_ARG( int, req
) );
3405 void PageView::slotSpeakDocument()
3408 QVector
< PageViewItem
* >::const_iterator it
= d
->items
.constBegin(), itEnd
= d
->items
.constEnd();
3409 for ( ; it
< itEnd
; ++it
)
3411 Okular::RegularAreaRect
* area
= textSelectionForItem( *it
);
3412 text
.append( (*it
)->page()->text( area
) );
3413 text
.append( '\n' );
3417 d
->tts()->say( text
);
3420 void PageView::slotSpeakCurrentPage()
3422 const int currentPage
= d
->document
->viewport().pageNumber
;
3424 PageViewItem
*item
= d
->items
.at( currentPage
);
3425 Okular::RegularAreaRect
* area
= textSelectionForItem( item
);
3426 const QString text
= item
->page()->text( area
);
3429 d
->tts()->say( text
);
3432 void PageView::slotStopSpeaks()
3437 d
->m_tts
->stopAllSpeechs();
3440 void PageView::slotAction( Okular::Action
*action
)
3442 d
->document
->processAction( action
);
3446 #include "pageview.moc"
3448 /* kate: replace-tabs on; indent-width 4; */