1 /***************************************************************************
2 * Copyright (C) 2004 by Enrico Ros <eros.kde@email.it> *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 ***************************************************************************/
10 #include "presentationwidget.h"
13 #include <QtDBus/QDBusConnection>
14 #include <QtDBus/QDBusMessage>
15 #include <QtDBus/QDBusReply>
18 #include <qfontmetrics.h>
25 #include <qstyleoption.h>
27 #include <qvalidator.h>
28 #include <qapplication.h>
29 #include <qdesktopwidget.h>
34 #include <kactioncollection.h>
35 #include <klineedit.h>
37 #include <kiconloader.h>
38 #include <kmessagebox.h>
39 #include <kselectaction.h>
40 #include <kshortcut.h>
48 #include "annotationtools.h"
49 #include "pagepainter.h"
50 #include "presentationsearchbar.h"
51 #include "videowidget.h"
52 #include "core/action.h"
53 #include "core/annotations.h"
54 #include "core/audioplayer.h"
55 #include "core/document.h"
56 #include "core/generator.h"
57 #include "core/movie.h"
58 #include "core/page.h"
62 // comment this to disable the top-right progress indicator
63 #define ENABLE_PROGRESS_OVERLAY
66 // a frame contains a pointer to the page object, its geometry and the
67 // transition effect to the next frame
68 struct PresentationFrame
72 qDeleteAll( videoWidgets
);
75 void recalcGeometry( int width
, int height
, float screenRatio
)
77 // calculate frame geometry keeping constant aspect ratio
78 float pageRatio
= page
->ratio();
79 int pageWidth
= width
,
81 if ( pageRatio
> screenRatio
)
82 pageWidth
= (int)( (float)pageHeight
/ pageRatio
);
84 pageHeight
= (int)( (float)pageWidth
* pageRatio
);
85 geometry
.setRect( ( width
- pageWidth
) / 2,
86 ( height
- pageHeight
) / 2,
87 pageWidth
, pageHeight
);
88 Q_FOREACH ( VideoWidget
*vw
, videoWidgets
)
90 const Okular::NormalizedRect r
= vw
->normGeometry();
91 QRect vwgeom
= r
.geometry( geometry
.width(), geometry
.height() );
92 vw
->resize( vwgeom
.size() );
93 vw
->move( geometry
.topLeft() + vwgeom
.topLeft() );
97 const Okular::Page
* page
;
99 QHash
< Okular::Movie
*, VideoWidget
* > videoWidgets
;
103 // a custom QToolBar that basically does not propagate the event if the widget
104 // background is not automatically filled
105 class PresentationToolBar
: public QToolBar
108 PresentationToolBar( QWidget
* parent
= 0 )
113 void mousePressEvent( QMouseEvent
* e
)
115 QToolBar::mousePressEvent( e
);
119 void mouseReleaseEvent( QMouseEvent
* e
)
121 QToolBar::mouseReleaseEvent( e
);
127 PresentationWidget::PresentationWidget( QWidget
* parent
, Okular::Document
* doc
, KActionCollection
* collection
)
128 : QWidget( 0 /* must be null, to have an independent widget */, Qt::FramelessWindowHint
),
129 m_pressedLink( 0 ), m_handCursor( false ), m_drawingEngine( 0 ), m_screenSaverCookie( -1 ),
130 m_parentWidget( parent
),
131 m_document( doc
), m_frameIndex( -1 ), m_topBar( 0 ), m_pagesEdit( 0 ), m_searchBar( 0 ),
132 m_screenSelect( 0 ), m_blockNotifications( false ), m_inBlackScreenMode( false )
135 setAttribute( Qt::WA_DeleteOnClose
);
136 setAttribute( Qt::WA_OpaquePaintEvent
);
137 setObjectName( "presentationWidget" );
138 QString caption
= doc
->metaData( "DocumentTitle" ).toString();
139 if ( caption
.trimmed().isEmpty() )
140 caption
= doc
->currentDocument().fileName();
141 setWindowTitle( KDialog::makeStandardCaption( caption
) );
146 // create top toolbar
147 m_topBar
= new PresentationToolBar( this );
148 m_topBar
->setObjectName( "presentationBar" );
149 m_topBar
->setIconSize( QSize( 32, 32 ) );
150 m_topBar
->setMovable( false );
151 m_topBar
->addAction( KIcon( layoutDirection() == Qt::RightToLeft
? "go-next" : "go-previous" ), i18n( "Previous Page" ), this, SLOT( slotPrevPage() ) );
152 m_pagesEdit
= new KLineEdit( m_topBar
);
153 QSizePolicy sp
= m_pagesEdit
->sizePolicy();
154 sp
.setHorizontalPolicy( QSizePolicy::Minimum
);
155 m_pagesEdit
->setSizePolicy( sp
);
156 QFontMetrics
fm( m_pagesEdit
->font() );
157 QStyleOptionFrame option
;
158 option
.initFrom( m_pagesEdit
);
159 m_pagesEdit
->setMaximumWidth( fm
.width( QString::number( m_document
->pages() ) ) + 2 * style()->pixelMetric( QStyle::PM_DefaultFrameWidth
, &option
, m_pagesEdit
) + 4 ); // the 4 comes from 2*horizontalMargin, horizontalMargin being a define in qlineedit.cpp
160 QIntValidator
*validator
= new QIntValidator( 1, m_document
->pages(), m_pagesEdit
);
161 m_pagesEdit
->setValidator( validator
);
162 m_topBar
->addWidget( m_pagesEdit
);
163 QLabel
*pagesLabel
= new QLabel( m_topBar
);
164 pagesLabel
->setText( QLatin1String( " / " ) + QString::number( m_document
->pages() ) + QLatin1String( " " ) );
165 m_topBar
->addWidget( pagesLabel
);
166 connect( m_pagesEdit
, SIGNAL( returnPressed() ), this, SLOT( slotPageChanged() ) );
167 m_topBar
->addAction( KIcon( layoutDirection() == Qt::RightToLeft
? "go-previous" : "go-next" ), i18n( "Next Page" ), this, SLOT( slotNextPage() ) );
168 m_topBar
->addSeparator();
169 QAction
*drawingAct
= collection
->action( "presentation_drawing_mode" );
170 connect( drawingAct
, SIGNAL( toggled( bool ) ), SLOT( togglePencilMode( bool ) ) );
171 m_topBar
->addAction( drawingAct
);
172 addAction( drawingAct
);
173 QAction
*eraseDrawingAct
= collection
->action( "presentation_erase_drawings" );
174 connect( eraseDrawingAct
, SIGNAL( triggered() ), SLOT( clearDrawings() ) );
175 m_topBar
->addAction( eraseDrawingAct
);
176 addAction( eraseDrawingAct
);
177 QDesktopWidget
*desktop
= QApplication::desktop();
178 if ( desktop
->numScreens() > 1 )
180 m_topBar
->addSeparator();
181 m_screenSelect
= new KSelectAction( KIcon( "video-display" ), i18n( "Switch Screen" ), m_topBar
);
182 m_screenSelect
->setToolBarMode( KSelectAction::MenuMode
);
183 m_screenSelect
->setToolButtonPopupMode( QToolButton::InstantPopup
);
184 m_topBar
->addAction( m_screenSelect
);
185 const int screenCount
= desktop
->numScreens();
186 for ( int i
= 0; i
< screenCount
; ++i
)
188 QAction
*act
= m_screenSelect
->addAction( i18nc( "%1 is the screen number (0, 1, ...)", "Screen %1", i
) );
189 act
->setData( qVariantFromValue( i
) );
192 QWidget
*spacer
= new QWidget( m_topBar
);
193 spacer
->setSizePolicy( QSizePolicy::Expanding
, QSizePolicy::MinimumExpanding
);
194 m_topBar
->addWidget( spacer
);
195 m_topBar
->addAction( KIcon( "application-exit" ), i18n( "Exit Presentation Mode" ), this, SLOT( close() ) );
196 m_topBar
->setAutoFillBackground( true );
198 // change topbar background color
199 QPalette p
= m_topBar
->palette();
200 p
.setColor( QPalette::Active
, QPalette::Button
, Qt::gray
);
201 p
.setColor( QPalette::Active
, QPalette::Background
, Qt::darkGray
);
202 m_topBar
->setPalette( p
);
205 setMouseTracking( true );
206 setContextMenuPolicy( Qt::PreventContextMenu
);
207 m_transitionTimer
= new QTimer( this );
208 m_transitionTimer
->setSingleShot( true );
209 connect( m_transitionTimer
, SIGNAL( timeout() ), this, SLOT( slotTransitionStep() ) );
210 m_overlayHideTimer
= new QTimer( this );
211 m_overlayHideTimer
->setSingleShot( true );
212 connect( m_overlayHideTimer
, SIGNAL( timeout() ), this, SLOT( slotHideOverlay() ) );
213 m_nextPageTimer
= new QTimer( this );
214 m_nextPageTimer
->setSingleShot( true );
215 connect( m_nextPageTimer
, SIGNAL( timeout() ), this, SLOT( slotNextPage() ) );
217 // handle cursor appearance as specified in configuration
218 if ( Okular::Settings::slidesCursor() == Okular::Settings::EnumSlidesCursor::HiddenDelay
)
220 KCursor::setAutoHideCursor( this, true );
221 KCursor::setHideCursorDelay( 3000 );
223 else if ( Okular::Settings::slidesCursor() == Okular::Settings::EnumSlidesCursor::Hidden
)
225 setCursor( QCursor( Qt::BlankCursor
) );
228 setupActions( collection
);
230 // inhibit the screen saver
231 inhibitScreenSaver();
233 QTimer::singleShot( 0, this, SLOT( slotDelayedEvents() ) );
236 PresentationWidget::~PresentationWidget()
238 // allow the screen saver again
241 // stop the audio playbacks
242 Okular::AudioPlayer::instance()->stopPlaybacks();
244 // remove our highlights
247 m_document
->resetSearch( PRESENTATION_SEARCH_ID
);
250 // remove this widget from document observer
251 m_document
->removeObserver( this );
253 QAction
*drawingAct
= m_ac
->action( "presentation_drawing_mode" );
254 disconnect( drawingAct
, 0, this, 0 );
255 if ( drawingAct
->isChecked() )
256 drawingAct
->toggle();
257 m_document
->removePageAnnotations( m_document
->viewport().pageNumber
, m_currentPageDrawings
);
258 delete m_drawingEngine
;
261 QVector
< PresentationFrame
* >::iterator fIt
= m_frames
.begin(), fEnd
= m_frames
.end();
262 for ( ; fIt
!= fEnd
; ++fIt
)
267 void PresentationWidget::notifySetup( const QVector
< Okular::Page
* > & pageSet
, int setupFlags
)
269 // same document, nothing to change - here we assume the document sets up
270 // us with the whole document set as first notifySetup()
271 if ( !( setupFlags
& Okular::DocumentObserver::DocumentChanged
) )
274 // delete previous frames (if any (shouldn't be))
275 QVector
< PresentationFrame
* >::iterator fIt
= m_frames
.begin(), fEnd
= m_frames
.end();
276 for ( ; fIt
!= fEnd
; ++fIt
)
278 if ( !m_frames
.isEmpty() )
279 kWarning() << "Frames setup changed while a Presentation is in progress.";
282 // create the new frames
283 QVector
< Okular::Page
* >::const_iterator setIt
= pageSet
.begin(), setEnd
= pageSet
.end();
284 float screenRatio
= (float)m_height
/ (float)m_width
;
285 for ( ; setIt
!= setEnd
; ++setIt
)
287 PresentationFrame
* frame
= new PresentationFrame();
288 frame
->page
= *setIt
;
289 const QLinkedList
< Okular::Annotation
* > annotations
= (*setIt
)->annotations();
290 QLinkedList
< Okular::Annotation
* >::const_iterator aIt
= annotations
.begin(), aEnd
= annotations
.end();
291 for ( ; aIt
!= aEnd
; ++aIt
)
293 Okular::Annotation
* a
= *aIt
;
294 if ( a
->subType() == Okular::Annotation::AMovie
)
296 Okular::MovieAnnotation
* movieAnn
= static_cast< Okular::MovieAnnotation
* >( a
);
297 VideoWidget
* vw
= new VideoWidget( movieAnn
, m_document
, this );
298 frame
->videoWidgets
.insert( movieAnn
->movie(), vw
);
302 frame
->recalcGeometry( m_width
, m_height
, screenRatio
);
303 // add the frame to the vector
304 m_frames
.push_back( frame
);
307 // get metadata from the document
308 m_metaStrings
.clear();
309 const Okular::DocumentInfo
* info
= m_document
->documentInfo();
312 if ( !info
->get( "title" ).isNull() )
313 m_metaStrings
+= i18n( "Title: %1", info
->get( "title" ) );
314 if ( !info
->get( "author" ).isNull() )
315 m_metaStrings
+= i18n( "Author: %1", info
->get( "author" ) );
317 m_metaStrings
+= i18n( "Pages: %1", m_document
->pages() );
318 m_metaStrings
+= i18n( "Click to begin" );
321 void PresentationWidget::notifyViewportChanged( bool /*smoothMove*/ )
323 // discard notifications if displaying the summary
324 if ( m_frameIndex
== -1 && Okular::Settings::slidesShowSummary() )
327 // display the current page
328 changePage( m_document
->viewport().pageNumber
);
330 // auto advance to the next page if set
331 startAutoChangeTimer();
334 void PresentationWidget::notifyPageChanged( int pageNumber
, int changedFlags
)
336 // if we are blocking the notifications, do nothing
337 if ( m_blockNotifications
)
340 // check if it's the last requested pixmap. if so update the widget.
341 if ( (changedFlags
& ( DocumentObserver::Pixmap
| DocumentObserver::Annotations
| DocumentObserver::Highlights
) ) && pageNumber
== m_frameIndex
)
342 generatePage( changedFlags
& ( DocumentObserver::Annotations
| DocumentObserver::Highlights
) );
345 bool PresentationWidget::canUnloadPixmap( int pageNumber
) const
347 if ( Okular::Settings::memoryLevel() != Okular::Settings::EnumMemoryLevel::Aggressive
)
349 // can unload all pixmaps except for the currently visible one
350 return pageNumber
!= m_frameIndex
;
354 // can unload all pixmaps except for the currently visible one, previous and next
355 return qAbs(pageNumber
- m_frameIndex
) <= 1;
359 void PresentationWidget::setupActions( KActionCollection
* collection
)
362 addAction( m_ac
->action( "go_previous" ) );
363 addAction( m_ac
->action( "go_next" ) );
364 addAction( m_ac
->action( "first_page" ) );
365 addAction( m_ac
->action( "last_page" ) );
367 QAction
*action
= m_ac
->action( "switch_blackscreen_mode" );
368 connect( action
, SIGNAL( toggled( bool ) ), SLOT( toggleBlackScreenMode( bool ) ) );
374 bool PresentationWidget::event( QEvent
* e
)
376 if ( e
->type() == QEvent::ToolTip
)
378 QHelpEvent
* he
= (QHelpEvent
*)e
;
381 const Okular::Action
* link
= getLink( he
->x(), he
->y(), &r
);
385 QString tip
= link
->actionTip();
386 if ( !tip
.isEmpty() )
387 QToolTip::showText( he
->globalPos(), tip
, this, r
);
393 // do not stop the event
394 return QWidget::event( e
);
397 void PresentationWidget::keyPressEvent( QKeyEvent
* e
)
399 if (m_width
== -1) return;
401 if ( e
->key() == Qt::Key_Left
|| e
->key() == Qt::Key_Backspace
|| e
->key() == Qt::Key_PageUp
)
403 else if ( e
->key() == Qt::Key_Right
|| e
->key() == Qt::Key_Space
|| e
->key() == Qt::Key_PageDown
)
405 else if ( e
->key() == Qt::Key_Home
)
407 else if ( e
->key() == Qt::Key_End
)
409 else if ( e
->key() == Qt::Key_Escape
)
411 if ( !m_topBar
->isHidden() )
418 void PresentationWidget::wheelEvent( QWheelEvent
* e
)
420 if (m_width
== -1) return;
422 // performance note: don't remove the clipping
423 int div
= e
->delta() / 120;
440 void PresentationWidget::mousePressEvent( QMouseEvent
* e
)
442 if (m_width
== -1) return;
444 if ( m_drawingEngine
)
446 QRect r
= routeMouseDrawingEvent( e
);
449 m_drawingRect
|= r
.translated( m_frames
[ m_frameIndex
]->geometry
.topLeft() );
450 update( m_drawingRect
);
455 // pressing left button
456 if ( e
->button() == Qt::LeftButton
)
458 // if pressing on a link, skip other checks
459 if ( ( m_pressedLink
= getLink( e
->x(), e
->y() ) ) )
462 // handle clicking on top-right overlay
463 if ( m_overlayGeometry
.contains( e
->pos() ) )
465 overlayClick( e
->pos() );
469 // if no other actions, go to next page
472 // pressing right button
473 else if ( e
->button() == Qt::RightButton
)
477 void PresentationWidget::mouseReleaseEvent( QMouseEvent
* e
)
479 if ( m_drawingEngine
)
481 QRect r
= routeMouseDrawingEvent( e
);
483 if ( m_drawingEngine
->creationCompleted() )
485 QList
< Okular::Annotation
* > annots
= m_drawingEngine
->end();
486 // manually disable and re-enable the pencil mode, so we can do
487 // cleaning of the actual drawer and create a new one just after
488 // that - that gives continuous drawing
489 togglePencilMode( false );
490 togglePencilMode( true );
491 foreach( Okular::Annotation
* ann
, annots
)
492 m_document
->addPageAnnotation( m_frameIndex
, ann
);
493 m_currentPageDrawings
<< annots
;
498 // if releasing on the same link we pressed over, execute it
499 if ( m_pressedLink
&& e
->button() == Qt::LeftButton
)
501 const Okular::Action
* link
= getLink( e
->x(), e
->y() );
502 if ( link
== m_pressedLink
)
503 m_document
->processAction( link
);
508 void PresentationWidget::mouseMoveEvent( QMouseEvent
* e
)
514 // update cursor and tooltip if hovering a link
515 if ( !m_drawingEngine
&& Okular::Settings::slidesCursor() != Okular::Settings::EnumSlidesCursor::Hidden
)
516 testCursorOnLink( e
->x(), e
->y() );
518 if ( !m_topBar
->isHidden() )
520 // hide a shown bar when exiting the area
521 if ( e
->y() > ( m_topBar
->height() + 1 ) )
524 setFocus( Qt::OtherFocusReason
);
529 if ( m_drawingEngine
&& e
->buttons() != Qt::NoButton
)
531 QRect r
= routeMouseDrawingEvent( e
);
533 m_drawingRect
|= r
.translated( m_frames
[ m_frameIndex
]->geometry
.topLeft() );
534 update( m_drawingRect
);
539 // show the bar if reaching top 2 pixels
542 // handle "dragging the wheel" if clicking on its geometry
543 else if ( ( QApplication::mouseButtons() & Qt::LeftButton
) && m_overlayGeometry
.contains( e
->pos() ) )
544 overlayClick( e
->pos() );
549 void PresentationWidget::paintEvent( QPaintEvent
* pe
)
551 if ( m_inBlackScreenMode
)
553 QPainter
painter( this );
554 painter
.fillRect( pe
->rect(), Qt::black
);
563 connect( m_document
, SIGNAL( linkFind() ), this, SLOT( slotFind() ) );
565 // register this observer in document. events will come immediately
566 m_document
->addObserver( this );
568 // show summary if requested
569 if ( Okular::Settings::slidesShowSummary() )
573 // check painting rect consistancy
574 QRect r
= pe
->rect().intersect( QRect( QPoint( 0, 0 ), geometry().size() ) );
578 if ( m_lastRenderedPixmap
.isNull() )
580 QPainter
painter( this );
581 painter
.fillRect( pe
->rect(), Okular::Settings::slidesBackgroundColor() );
585 // blit the pixmap to the screen
586 QVector
<QRect
> allRects
= pe
->region().rects();
587 uint numRects
= allRects
.count();
588 QPainter
painter( this );
589 for ( uint i
= 0; i
< numRects
; i
++ )
591 const QRect
& r
= allRects
[i
];
594 #ifdef ENABLE_PROGRESS_OVERLAY
595 if ( Okular::Settings::slidesShowProgress() && r
.intersects( m_overlayGeometry
) )
597 // backbuffer the overlay operation
598 QPixmap
backPixmap( r
.size() );
599 QPainter
pixPainter( &backPixmap
);
601 // first draw the background on the backbuffer
602 pixPainter
.drawPixmap( QPoint(0,0), m_lastRenderedPixmap
, r
);
604 // then blend the overlay (a piece of) over the background
605 QRect ovr
= m_overlayGeometry
.intersect( r
);
606 pixPainter
.drawPixmap( ovr
.left() - r
.left(), ovr
.top() - r
.top(),
607 m_lastRenderedOverlay
, ovr
.left() - m_overlayGeometry
.left(),
608 ovr
.top() - m_overlayGeometry
.top(), ovr
.width(), ovr
.height() );
610 // finally blit the pixmap to the screen
612 painter
.drawPixmap( r
.topLeft(), backPixmap
, backPixmap
.rect() );
615 // copy the rendered pixmap to the screen
616 painter
.drawPixmap( r
.topLeft(), m_lastRenderedPixmap
, r
);
618 if ( m_drawingEngine
&& m_drawingRect
.intersects( pe
->rect() ) )
620 const QRect
& geom
= m_frames
[ m_frameIndex
]->geometry
;
622 painter
.translate( geom
.topLeft() );
623 m_drawingEngine
->paint( &painter
, geom
.width(), geom
.height(), m_drawingRect
.intersect( pe
->rect() ) );
629 void PresentationWidget::resizeEvent( QResizeEvent
*re
)
631 // kDebug() << re->oldSize() << "=>" << re->size();
632 if ( re
->oldSize() == QSize( -1, -1 ) )
635 m_screen
= QApplication::desktop()->screenNumber( this );
637 applyNewScreenSize( re
->oldSize() );
642 const Okular::Action
* PresentationWidget::getLink( int x
, int y
, QRect
* geometry
) const
644 // no links on invalid pages
645 if ( geometry
&& !geometry
->isNull() )
646 geometry
->setRect( 0, 0, 0, 0 );
647 if ( m_frameIndex
< 0 || m_frameIndex
>= (int)m_frames
.size() )
650 // get frame, page and geometry
651 const PresentationFrame
* frame
= m_frames
[ m_frameIndex
];
652 const Okular::Page
* page
= frame
->page
;
653 const QRect
& frameGeometry
= frame
->geometry
;
655 // compute normalized x and y
656 double nx
= (double)(x
- frameGeometry
.left()) / (double)frameGeometry
.width();
657 double ny
= (double)(y
- frameGeometry
.top()) / (double)frameGeometry
.height();
659 // no links outside the pages
660 if ( nx
< 0 || nx
> 1 || ny
< 0 || ny
> 1 )
663 // check if 1) there is an object and 2) it's a link
664 const QRect d
= QApplication::desktop()->screenGeometry( m_screen
);
665 const Okular::ObjectRect
* object
= page
->objectRect( Okular::ObjectRect::Action
, nx
, ny
, d
.width(), d
.height() );
669 // compute link geometry if destination rect present
672 *geometry
= object
->boundingRect( frameGeometry
.width(), frameGeometry
.height() );
673 geometry
->translate( frameGeometry
.left(), frameGeometry
.top() );
676 // return the link pointer
677 return (Okular::Action
*)object
->object();
680 void PresentationWidget::testCursorOnLink( int x
, int y
)
682 const Okular::Action
* link
= getLink( x
, y
, 0 );
684 // only react on changes (in/out from a link)
685 if ( (link
&& !m_handCursor
) || (!link
&& m_handCursor
) )
687 // change cursor shape
688 m_handCursor
= link
!= 0;
689 setCursor( QCursor( m_handCursor
? Qt::PointingHandCursor
: Qt::ArrowCursor
) );
693 void PresentationWidget::overlayClick( const QPoint
& position
)
695 // clicking the progress indicator
696 int xPos
= position
.x() - m_overlayGeometry
.x() - m_overlayGeometry
.width() / 2,
697 yPos
= m_overlayGeometry
.height() / 2 - position
.y();
698 if ( !xPos
&& !yPos
)
701 // compute angle relative to indicator (note coord transformation)
702 float angle
= 0.5 + 0.5 * atan2( (double)-xPos
, (double)-yPos
) / M_PI
;
703 int pageIndex
= (int)( angle
* ( m_frames
.count() - 1 ) + 0.5 );
705 // go to selected page
706 changePage( pageIndex
);
709 void PresentationWidget::changePage( int newPage
)
711 if ( m_frameIndex
== newPage
)
714 const int oldIndex
= m_frameIndex
;
715 // check if pixmap exists or else request it
716 m_frameIndex
= newPage
;
717 PresentationFrame
* frame
= m_frames
[ m_frameIndex
];
718 int pixW
= frame
->geometry
.width();
719 int pixH
= frame
->geometry
.height();
721 bool signalsBlocked
= m_pagesEdit
->signalsBlocked();
722 m_pagesEdit
->blockSignals( true );
723 m_pagesEdit
->setText( QString::number( m_frameIndex
+ 1 ) );
724 m_pagesEdit
->blockSignals( signalsBlocked
);
726 // if pixmap not inside the Okular::Page we request it and wait for
727 // notifyPixmapChanged call or else we can proceed to pixmap generation
728 if ( !frame
->page
->hasPixmap( PRESENTATION_ID
, pixW
, pixH
) )
734 // make the background pixmap
738 // set a new viewport in document if page number differs
739 if ( m_frameIndex
!= -1 && m_frameIndex
!= m_document
->viewport().pageNumber
)
741 // stop the audio playback, if any
742 Okular::AudioPlayer::instance()->stopPlaybacks();
743 // perform the page closing action, if any
744 if ( m_document
->page( m_document
->viewport().pageNumber
)->pageAction( Okular::Page::Closing
) )
745 m_document
->processAction( m_document
->page( m_document
->viewport().pageNumber
)->pageAction( Okular::Page::Closing
) );
747 // remove the drawing on the old page before switching
749 m_document
->setViewportPage( m_frameIndex
, PRESENTATION_ID
);
751 // perform the page opening action, if any
752 if ( m_document
->page( m_frameIndex
)->pageAction( Okular::Page::Opening
) )
753 m_document
->processAction( m_document
->page( m_frameIndex
)->pageAction( Okular::Page::Opening
) );
757 if ( oldIndex
!= m_frameIndex
)
759 if ( oldIndex
!= -1 )
761 Q_FOREACH ( VideoWidget
*vw
, m_frames
[ oldIndex
]->videoWidgets
)
768 Q_FOREACH ( VideoWidget
*vw
, m_frames
[ m_frameIndex
]->videoWidgets
)
777 void PresentationWidget::generatePage( bool disableTransition
)
779 if ( m_lastRenderedPixmap
.isNull() )
780 m_lastRenderedPixmap
= QPixmap( m_width
, m_height
);
782 // opens the painter over the pixmap
783 QPainter pixmapPainter
;
784 pixmapPainter
.begin( &m_lastRenderedPixmap
);
785 // generate welcome page
786 if ( m_frameIndex
== -1 )
787 generateIntroPage( pixmapPainter
);
788 // generate a normal pixmap with extended margin filling
789 if ( m_frameIndex
>= 0 && m_frameIndex
< (int)m_document
->pages() )
790 generateContentsPage( m_frameIndex
, pixmapPainter
);
793 // generate the top-right corner overlay
794 #ifdef ENABLE_PROGRESS_OVERLAY
795 if ( Okular::Settings::slidesShowProgress() && m_frameIndex
!= -1 )
799 // start transition on pages that have one
800 if ( !disableTransition
&& Okular::Settings::slidesTransitionsEnabled() )
802 const Okular::PageTransition
* transition
= m_frameIndex
!= -1 ?
803 m_frames
[ m_frameIndex
]->page
->transition() : 0;
805 initTransition( transition
);
807 Okular::PageTransition trans
= defaultTransition();
808 initTransition( &trans
);
813 Okular::PageTransition trans
= defaultTransition( Okular::Settings::EnumSlidesTransition::Replace
);
814 initTransition( &trans
);
817 // update cursor + tooltip
818 if ( Okular::Settings::slidesCursor() != Okular::Settings::EnumSlidesCursor::Hidden
)
820 QPoint p
= mapFromGlobal( QCursor::pos() );
821 testCursorOnLink( p
.x(), p
.y() );
825 void PresentationWidget::generateIntroPage( QPainter
& p
)
827 // use a vertical gray gradient background
828 int blend1
= m_height
/ 10,
829 blend2
= 9 * m_height
/ 10;
830 int baseTint
= QColor(Qt::gray
).red();
831 for ( int i
= 0; i
< m_height
; i
++ )
835 k
-= (int)( baseTint
* (i
-blend1
)*(i
-blend1
) / (float)(blend1
* blend1
) );
837 k
+= (int)( (255-baseTint
) * (i
-blend2
)*(i
-blend2
) / (float)(blend1
* blend1
) );
838 p
.fillRect( 0, i
, m_width
, 1, QColor( k
, k
, k
) );
841 // draw okular logo in the four corners
842 QPixmap logo
= DesktopIcon( "okular", 64 );
843 if ( !logo
.isNull() )
845 p
.drawPixmap( 5, 5, logo
);
846 p
.drawPixmap( m_width
- 5 - logo
.width(), 5, logo
);
847 p
.drawPixmap( m_width
- 5 - logo
.width(), m_height
- 5 - logo
.height(), logo
);
848 p
.drawPixmap( 5, m_height
- 5 - logo
.height(), logo
);
851 // draw metadata text (the last line is 'click to begin')
852 int strNum
= m_metaStrings
.count(),
853 strHeight
= m_height
/ ( strNum
+ 4 ),
854 fontHeight
= 2 * strHeight
/ 3;
855 QFont
font( p
.font() );
856 font
.setPixelSize( fontHeight
);
857 QFontMetrics
metrics( font
);
858 for ( int i
= 0; i
< strNum
; i
++ )
860 // set a font to fit text width
861 float wScale
= (float)metrics
.boundingRect( m_metaStrings
[i
] ).width() / (float)m_width
;
864 f
.setPixelSize( (int)( (float)fontHeight
/ (float)wScale
) );
868 p
.setPen( Qt::darkGray
);
869 p
.drawText( 2, m_height
/ 4 + strHeight
* i
+ 2, m_width
, strHeight
,
870 Qt::AlignHCenter
| Qt::AlignVCenter
, m_metaStrings
[i
] );
872 p
.setPen( 128 + (127 * i
) / strNum
);
873 p
.drawText( 0, m_height
/ 4 + strHeight
* i
, m_width
, strHeight
,
874 Qt::AlignHCenter
| Qt::AlignVCenter
, m_metaStrings
[i
] );
878 void PresentationWidget::generateContentsPage( int pageNum
, QPainter
& p
)
880 PresentationFrame
* frame
= m_frames
[ pageNum
];
882 // translate painter and contents rect
883 QRect
geom( frame
->geometry
);
884 p
.translate( geom
.left(), geom
.top() );
885 geom
.translate( -geom
.left(), -geom
.top() );
887 // draw the page using the shared PagePainter class
888 int flags
= PagePainter::Accessibility
| PagePainter::Highlights
| PagePainter::Annotations
;
889 PagePainter::paintPageOnPainter( &p
, frame
->page
, PRESENTATION_ID
, flags
,
890 geom
.width(), geom
.height(), geom
);
893 p
.translate( -frame
->geometry
.left(), -frame
->geometry
.top() );
895 // fill unpainted areas with background color
896 QRegion
unpainted( QRect( 0, 0, m_width
, m_height
) );
897 QVector
<QRect
> rects
= unpainted
.subtract( frame
->geometry
).rects();
898 for ( int i
= 0; i
< rects
.count(); i
++ )
900 const QRect
& r
= rects
[i
];
901 p
.fillRect( r
, Okular::Settings::slidesBackgroundColor() );
905 // from Arthur - Qt4 - (is defined elsewhere as 'qt_div_255' to not break final compilation)
906 inline int qt_div255(int x
) { return (x
+ (x
>>8) + 0x80) >> 8; }
907 void PresentationWidget::generateOverlay()
909 #ifdef ENABLE_PROGRESS_OVERLAY
910 // calculate overlay geometry and resize pixmap if needed
911 int side
= m_width
/ 16;
912 m_overlayGeometry
.setRect( m_width
- side
- 4, 4, side
, side
);
914 // note: to get a sort of antialiasing, we render the pixmap double sized
915 // and the resulting image is smoothly scaled down. So here we open a
916 // painter on the double sized pixmap.
918 QPixmap
doublePixmap( side
, side
);
919 doublePixmap
.fill( Qt::black
);
920 QPainter
pixmapPainter( &doublePixmap
);
921 pixmapPainter
.setRenderHints( QPainter::Antialiasing
);
923 // draw PIE SLICES in blue levels (the levels will then be the alpha component)
924 int pages
= m_document
->pages();
926 { // draw continuous slices
927 int degrees
= (int)( 360 * (float)(m_frameIndex
+ 1) / (float)pages
);
928 pixmapPainter
.setPen( 0x05 );
929 pixmapPainter
.setBrush( QColor( 0x40 ) );
930 pixmapPainter
.drawPie( 2, 2, side
- 4, side
- 4, 90*16, (360-degrees
)*16 );
931 pixmapPainter
.setPen( 0x40 );
932 pixmapPainter
.setBrush( QColor( 0xF0 ) );
933 pixmapPainter
.drawPie( 2, 2, side
- 4, side
- 4, 90*16, -degrees
*16 );
936 { // draw discrete slices
937 float oldCoord
= -90;
938 for ( int i
= 0; i
< pages
; i
++ )
940 float newCoord
= -90 + 360 * (float)(i
+ 1) / (float)pages
;
941 pixmapPainter
.setPen( i
<= m_frameIndex
? 0x40 : 0x05 );
942 pixmapPainter
.setBrush( QColor( i
<= m_frameIndex
? 0xF0 : 0x40 ) );
943 pixmapPainter
.drawPie( 2, 2, side
- 4, side
- 4,
944 (int)( -16*(oldCoord
+ 1) ), (int)( -16*(newCoord
- (oldCoord
+ 2)) ) );
948 int circleOut
= side
/ 4;
949 pixmapPainter
.setPen( Qt::black
);
950 pixmapPainter
.setBrush( Qt::black
);
951 pixmapPainter
.drawEllipse( circleOut
, circleOut
, side
- 2*circleOut
, side
- 2*circleOut
);
953 // draw TEXT using maximum opacity
954 QFont
f( pixmapPainter
.font() );
955 f
.setPixelSize( side
/ 4 );
956 pixmapPainter
.setFont( f
);
957 pixmapPainter
.setPen( 0xFF );
958 // use a little offset to prettify output
959 pixmapPainter
.drawText( 2, 2, side
, side
, Qt::AlignCenter
, QString::number( m_frameIndex
+ 1 ) );
961 // end drawing pixmap and halve image
963 QImage
image( doublePixmap
.toImage().scaled( side
/ 2, side
/ 2, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
) );
964 image
= image
.convertToFormat( QImage::Format_ARGB32
);
966 // draw circular shadow using the same technique
967 doublePixmap
.fill( Qt::black
);
968 pixmapPainter
.begin( &doublePixmap
);
969 pixmapPainter
.setPen( 0x40 );
970 pixmapPainter
.setBrush( QColor( 0x80 ) );
971 pixmapPainter
.drawEllipse( 0, 0, side
, side
);
973 QImage
shadow( doublePixmap
.toImage().scaled( side
/ 2, side
/ 2, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
) );
975 // generate a 2 colors pixmap using mixing shadow (made with highlight color)
976 // and image (made with highlightedText color)
977 QPalette pal
= palette();
978 QColor color
= pal
.color( QPalette::Active
, QPalette::HighlightedText
);
979 int red
= color
.red(), green
= color
.green(), blue
= color
.blue();
980 color
= pal
.color( QPalette::Active
, QPalette::Highlight
);
981 int sRed
= color
.red(), sGreen
= color
.green(), sBlue
= color
.blue();
983 unsigned int * data
= (unsigned int *)image
.bits(),
984 * shadowData
= (unsigned int *)shadow
.bits(),
985 pixels
= image
.width() * image
.height();
986 // cache data (reduce computation time to 26%!)
987 int c1
= -1, c2
= -1, cR
= 0, cG
= 0, cB
= 0, cA
= 0;
989 for( unsigned int i
= 0; i
< pixels
; ++i
)
991 // alpha for shadow and image
992 int shadowAlpha
= shadowData
[i
] & 0xFF,
993 srcAlpha
= data
[i
] & 0xFF;
995 if ( srcAlpha
!= c1
|| shadowAlpha
!= c2
)
999 // fuse color components and alpha value of image over shadow
1001 cR
= qt_div255( srcAlpha
* red
+ (255 - srcAlpha
) * sRed
),
1002 cG
= qt_div255( srcAlpha
* green
+ (255 - srcAlpha
) * sGreen
),
1003 cB
= qt_div255( srcAlpha
* blue
+ (255 - srcAlpha
) * sBlue
),
1004 cA
= qt_div255( srcAlpha
* srcAlpha
+ (255 - srcAlpha
) * shadowAlpha
)
1008 data
[i
] = qRgba( cR
, cG
, cB
, cA
);
1010 m_lastRenderedOverlay
= QPixmap::fromImage( image
);
1012 // start the autohide timer
1013 //repaint( m_overlayGeometry ); // toggle with next line
1014 update( m_overlayGeometry
);
1015 m_overlayHideTimer
->start( 2500 );
1020 QRect
PresentationWidget::routeMouseDrawingEvent( QMouseEvent
* e
)
1022 const QRect
& geom
= m_frames
[ m_frameIndex
]->geometry
;
1023 const Okular::Page
* page
= m_frames
[ m_frameIndex
]->page
;
1025 AnnotatorEngine::EventType eventType
= AnnotatorEngine::Press
;
1026 if ( e
->type() == QEvent::MouseMove
)
1027 eventType
= AnnotatorEngine::Move
;
1028 else if ( e
->type() == QEvent::MouseButtonRelease
)
1029 eventType
= AnnotatorEngine::Release
;
1031 // find out the pressed button
1032 AnnotatorEngine::Button button
= AnnotatorEngine::None
;
1033 Qt::MouseButtons buttonState
= ( eventType
== AnnotatorEngine::Move
) ? e
->buttons() : e
->button();
1034 if ( buttonState
== Qt::LeftButton
)
1035 button
= AnnotatorEngine::Left
;
1036 else if ( buttonState
== Qt::RightButton
)
1037 button
= AnnotatorEngine::Right
;
1038 static bool hasclicked
= false;
1039 if ( eventType
== AnnotatorEngine::Press
)
1042 double nX
= ( (double)e
->x() - (double)geom
.left() ) / (double)geom
.width();
1043 double nY
= ( (double)e
->y() - (double)geom
.top() ) / (double)geom
.height();
1045 if ( hasclicked
&& nX
>= 0 && nX
< 1 && nY
>= 0 && nY
< 1 )
1046 ret
= m_drawingEngine
->event( eventType
, button
, nX
, nY
, geom
.width(), geom
.height(), page
);
1048 if ( eventType
== AnnotatorEngine::Release
)
1054 void PresentationWidget::startAutoChangeTimer()
1056 double pageDuration
= m_frameIndex
>= 0 && m_frameIndex
< (int)m_frames
.count() ? m_frames
[ m_frameIndex
]->page
->duration() : -1;
1057 if ( Okular::Settings::slidesAdvance() || pageDuration
>= 0.0 )
1059 double secs
= pageDuration
< 0.0
1060 ? Okular::Settings::slidesAdvanceTime()
1061 : qMin
<double>( pageDuration
, Okular::Settings::slidesAdvanceTime() );
1062 m_nextPageTimer
->start( (int)( secs
* 1000 ) );
1066 void PresentationWidget::recalcGeometry()
1068 QDesktopWidget
*desktop
= QApplication::desktop();
1069 const int preferenceScreen
= Okular::Settings::slidesScreen();
1071 if ( preferenceScreen
== -2 )
1073 screen
= desktop
->screenNumber( m_parentWidget
);
1075 else if ( preferenceScreen
== -1 )
1077 screen
= desktop
->primaryScreen();
1079 else if ( preferenceScreen
>= 0 && preferenceScreen
< desktop
->numScreens() )
1081 screen
= preferenceScreen
;
1085 screen
= desktop
->screenNumber( m_parentWidget
);
1086 Okular::Settings::setSlidesScreen( -2 );
1088 const QRect screenGeom
= desktop
->screenGeometry( screen
);
1089 // kDebug() << screen << "=>" << screenGeom;
1091 setGeometry( screenGeom
);
1094 void PresentationWidget::repositionContent()
1096 const QRect ourGeom
= geometry();
1098 m_topBar
->setGeometry( 0, 0, ourGeom
.width(), 32 + 10 );
1101 void PresentationWidget::requestPixmaps()
1103 PresentationFrame
* frame
= m_frames
[ m_frameIndex
];
1104 int pixW
= frame
->geometry
.width();
1105 int pixH
= frame
->geometry
.height();
1107 // operation will take long: set busy cursor
1108 QApplication::setOverrideCursor( QCursor( Qt::BusyCursor
) );
1109 // request the pixmap
1110 QLinkedList
< Okular::PixmapRequest
* > requests
;
1111 requests
.push_back( new Okular::PixmapRequest( PRESENTATION_ID
, m_frameIndex
, pixW
, pixH
, PRESENTATION_PRIO
, false ) );
1113 QApplication::restoreOverrideCursor();
1114 // ask for next and previous page if not in low memory usage setting
1115 if ( Okular::Settings::memoryLevel() != Okular::Settings::EnumMemoryLevel::Low
&& Okular::Settings::enableThreading() )
1117 if ( m_frameIndex
+ 1 < (int)m_document
->pages() )
1119 PresentationFrame
*nextFrame
= m_frames
[ m_frameIndex
+ 1 ];
1120 pixW
= nextFrame
->geometry
.width();
1121 pixH
= nextFrame
->geometry
.height();
1122 if ( !nextFrame
->page
->hasPixmap( PRESENTATION_ID
, pixW
, pixH
) )
1123 requests
.push_back( new Okular::PixmapRequest( PRESENTATION_ID
, m_frameIndex
+ 1, pixW
, pixH
, PRESENTATION_PRELOAD_PRIO
, true ) );
1125 if ( m_frameIndex
- 1 >= 0 )
1127 PresentationFrame
*prevFrame
= m_frames
[ m_frameIndex
- 1 ];
1128 pixW
= prevFrame
->geometry
.width();
1129 pixH
= prevFrame
->geometry
.height();
1130 if ( !prevFrame
->page
->hasPixmap( PRESENTATION_ID
, pixW
, pixH
) )
1131 requests
.push_back( new Okular::PixmapRequest( PRESENTATION_ID
, m_frameIndex
- 1, pixW
, pixH
, PRESENTATION_PRELOAD_PRIO
, true ) );
1134 m_document
->requestPixmaps( requests
);
1138 void PresentationWidget::slotNextPage()
1140 // loop when configured
1141 if ( m_frameIndex
== (int)m_frames
.count() - 1 && Okular::Settings::slidesLoop() )
1144 if ( m_frameIndex
< (int)m_frames
.count() - 1 )
1147 changePage( m_frameIndex
+ 1 );
1148 // auto advance to the next page if set
1149 startAutoChangeTimer();
1153 #ifdef ENABLE_PROGRESS_OVERLAY
1154 if ( Okular::Settings::slidesShowProgress() )
1157 if ( m_transitionTimer
->isActive() )
1159 m_transitionTimer
->stop();
1163 // we need the setFocus() call here to let KCursor::autoHide() work correctly
1167 void PresentationWidget::slotPrevPage()
1169 if ( m_frameIndex
> 0 )
1171 // go to previous page
1172 changePage( m_frameIndex
- 1 );
1174 // auto advance to the next page if set
1175 startAutoChangeTimer();
1179 #ifdef ENABLE_PROGRESS_OVERLAY
1180 if ( Okular::Settings::slidesShowProgress() )
1183 if ( m_transitionTimer
->isActive() )
1185 m_transitionTimer
->stop();
1191 void PresentationWidget::slotFirstPage()
1196 void PresentationWidget::slotLastPage()
1198 changePage( (int)m_frames
.count() - 1 );
1201 void PresentationWidget::slotHideOverlay()
1203 QRect
geom( m_overlayGeometry
);
1204 m_overlayGeometry
.setCoords( 0, 0, -1, -1 );
1208 void PresentationWidget::slotTransitionStep()
1210 if ( m_transitionRects
.empty() )
1212 // it's better to fix the transition to cover the whole screen than
1213 // enabling the following line that wastes cpu for nothing
1218 for ( int i
= 0; i
< m_transitionMul
&& !m_transitionRects
.empty(); i
++ )
1220 update( m_transitionRects
.first() );
1221 m_transitionRects
.pop_front();
1223 m_transitionTimer
->start( m_transitionDelay
);
1226 void PresentationWidget::slotDelayedEvents()
1229 repositionContent();
1231 if ( m_screenSelect
)
1233 m_screenSelect
->setCurrentItem( m_screen
);
1234 connect( m_screenSelect
->selectableActionGroup(), SIGNAL( triggered( QAction
* ) ),
1235 this, SLOT( chooseScreen( QAction
* ) ) );
1238 // show widget and take control
1240 setWindowState( windowState() | Qt::WindowFullScreen
);
1242 connect( QApplication::desktop(), SIGNAL( resized( int ) ), this, SLOT( screenResized( int ) ) );
1244 // inform user on how to exit from presentation mode
1245 KMessageBox::information( this, i18n("There are two ways of exiting presentation mode, you can press either ESC key or click with the quit button that appears when placing the mouse in the top-right corner. Of course you can cycle windows (Alt+TAB by default)"), QString(), "presentationInfo" );
1248 void PresentationWidget::slotPageChanged()
1251 int p
= m_pagesEdit
->text().toInt( &ok
);
1255 changePage( p
- 1 );
1258 void PresentationWidget::togglePencilMode( bool on
)
1262 QString colorstring
= Okular::Settings::slidesPencilColor().name();
1263 // FIXME this should not be recreated every time
1264 QDomDocument
doc( "engine" );
1265 QDomElement root
= doc
.createElement( "engine" );
1266 root
.setAttribute( "color", colorstring
);
1267 doc
.appendChild( root
);
1268 QDomElement annElem
= doc
.createElement( "annotation" );
1269 root
.appendChild( annElem
);
1270 annElem
.setAttribute( "type", "Ink" );
1271 annElem
.setAttribute( "color", colorstring
);
1272 annElem
.setAttribute( "width", "2" );
1273 m_drawingEngine
= new SmoothPathEngine( root
);
1274 setCursor( KCursor( "pencil", Qt::ArrowCursor
) );
1278 delete m_drawingEngine
;
1279 m_drawingEngine
= 0;
1280 m_drawingRect
= QRect();
1281 setCursor( Qt::ArrowCursor
);
1285 void PresentationWidget::clearDrawings()
1287 m_document
->removePageAnnotations( m_document
->viewport().pageNumber
, m_currentPageDrawings
);
1288 m_currentPageDrawings
.clear();
1291 void PresentationWidget::screenResized( int screen
)
1293 // we can ignore if a screen was resized in the case the screen is not
1295 if ( screen
!= m_screen
)
1298 setScreen( screen
);
1301 void PresentationWidget::chooseScreen( QAction
*act
)
1303 if ( !act
|| act
->data().type() != QVariant::Int
)
1306 const int newScreen
= act
->data().toInt();
1308 setScreen( newScreen
);
1311 void PresentationWidget::toggleBlackScreenMode( bool )
1313 m_inBlackScreenMode
= !m_inBlackScreenMode
;
1318 void PresentationWidget::setScreen( int newScreen
)
1320 const QRect screenGeom
= QApplication::desktop()->screenGeometry( newScreen
);
1321 const QSize oldSize
= size();
1322 // kDebug() << newScreen << "=>" << screenGeom;
1323 m_screen
= newScreen
;
1324 setGeometry( screenGeom
);
1326 applyNewScreenSize( oldSize
);
1329 void PresentationWidget::applyNewScreenSize( const QSize
& oldSize
)
1331 repositionContent();
1333 // if by chance the new screen has the same resolution of the previous,
1334 // do not invalidate pixmaps and such..
1335 if ( size() == oldSize
)
1339 m_height
= height();
1341 // update the frames
1342 QVector
< PresentationFrame
* >::const_iterator fIt
= m_frames
.constBegin(), fEnd
= m_frames
.constEnd();
1343 const float screenRatio
= (float)m_height
/ (float)m_width
;
1344 for ( ; fIt
!= fEnd
; ++fIt
)
1346 (*fIt
)->recalcGeometry( m_width
, m_height
, screenRatio
);
1349 if ( m_frameIndex
!= -1 )
1352 const_cast< Okular::Page
* >( m_frames
[ m_frameIndex
]->page
)->deletePixmap( PRESENTATION_ID
);
1353 // force the regeneration of the pixmap
1354 m_lastRenderedPixmap
= QPixmap();
1355 m_blockNotifications
= true;
1357 m_blockNotifications
= false;
1359 generatePage( true /* no transitions */ );
1362 void PresentationWidget::inhibitScreenSaver()
1364 QDBusMessage message
= QDBusMessage::createMethodCall( "org.freedesktop.ScreenSaver", "/ScreenSaver",
1365 "org.freedesktop.ScreenSaver", "Inhibit" );
1366 message
<< QString( "Okular" );
1367 message
<< i18nc( "Reason for inhibiting the screensaver activation, when the presentation mode is active", "Giving a presentation" );
1369 QDBusReply
<uint
> reply
= QDBusConnection::sessionBus().call( message
);
1370 if ( reply
.isValid() )
1371 m_screenSaverCookie
= reply
.value();
1374 void PresentationWidget::allowScreenSaver()
1376 if ( m_screenSaverCookie
!= -1 )
1378 QDBusMessage message
= QDBusMessage::createMethodCall( "org.freedesktop.ScreenSaver", "/ScreenSaver",
1379 "org.freedesktop.ScreenSaver", "UnInhibit" );
1380 message
<< (uint
)m_screenSaverCookie
;
1381 QDBusConnection::sessionBus().send( message
);
1385 void PresentationWidget::slotFind()
1389 m_searchBar
= new PresentationSearchBar( m_document
, this, this );
1390 m_searchBar
->forceSnap();
1392 m_searchBar
->focusOnSearchEdit();
1393 m_searchBar
->show();
1397 const Okular::PageTransition
PresentationWidget::defaultTransition() const
1399 return defaultTransition( Okular::Settings::slidesTransition() );
1402 const Okular::PageTransition
PresentationWidget::defaultTransition( int type
) const
1406 case Okular::Settings::EnumSlidesTransition::BlindsHorizontal
:
1408 Okular::PageTransition
transition( Okular::PageTransition::Blinds
);
1409 transition
.setAlignment( Okular::PageTransition::Horizontal
);
1413 case Okular::Settings::EnumSlidesTransition::BlindsVertical
:
1415 Okular::PageTransition
transition( Okular::PageTransition::Blinds
);
1416 transition
.setAlignment( Okular::PageTransition::Vertical
);
1420 case Okular::Settings::EnumSlidesTransition::BoxIn
:
1422 Okular::PageTransition
transition( Okular::PageTransition::Box
);
1423 transition
.setDirection( Okular::PageTransition::Inward
);
1427 case Okular::Settings::EnumSlidesTransition::BoxOut
:
1429 Okular::PageTransition
transition( Okular::PageTransition::Box
);
1430 transition
.setDirection( Okular::PageTransition::Outward
);
1434 case Okular::Settings::EnumSlidesTransition::Dissolve
:
1436 return Okular::PageTransition( Okular::PageTransition::Dissolve
);
1439 case Okular::Settings::EnumSlidesTransition::GlitterDown
:
1441 Okular::PageTransition
transition( Okular::PageTransition::Glitter
);
1442 transition
.setAngle( 270 );
1446 case Okular::Settings::EnumSlidesTransition::GlitterRight
:
1448 Okular::PageTransition
transition( Okular::PageTransition::Glitter
);
1449 transition
.setAngle( 0 );
1453 case Okular::Settings::EnumSlidesTransition::GlitterRightDown
:
1455 Okular::PageTransition
transition( Okular::PageTransition::Glitter
);
1456 transition
.setAngle( 315 );
1460 case Okular::Settings::EnumSlidesTransition::Random
:
1462 return defaultTransition( KRandom::random() % 18 );
1465 case Okular::Settings::EnumSlidesTransition::SplitHorizontalIn
:
1467 Okular::PageTransition
transition( Okular::PageTransition::Split
);
1468 transition
.setAlignment( Okular::PageTransition::Horizontal
);
1469 transition
.setDirection( Okular::PageTransition::Inward
);
1473 case Okular::Settings::EnumSlidesTransition::SplitHorizontalOut
:
1475 Okular::PageTransition
transition( Okular::PageTransition::Split
);
1476 transition
.setAlignment( Okular::PageTransition::Horizontal
);
1477 transition
.setDirection( Okular::PageTransition::Outward
);
1481 case Okular::Settings::EnumSlidesTransition::SplitVerticalIn
:
1483 Okular::PageTransition
transition( Okular::PageTransition::Split
);
1484 transition
.setAlignment( Okular::PageTransition::Vertical
);
1485 transition
.setDirection( Okular::PageTransition::Inward
);
1489 case Okular::Settings::EnumSlidesTransition::SplitVerticalOut
:
1491 Okular::PageTransition
transition( Okular::PageTransition::Split
);
1492 transition
.setAlignment( Okular::PageTransition::Vertical
);
1493 transition
.setDirection( Okular::PageTransition::Outward
);
1497 case Okular::Settings::EnumSlidesTransition::WipeDown
:
1499 Okular::PageTransition
transition( Okular::PageTransition::Wipe
);
1500 transition
.setAngle( 270 );
1504 case Okular::Settings::EnumSlidesTransition::WipeRight
:
1506 Okular::PageTransition
transition( Okular::PageTransition::Wipe
);
1507 transition
.setAngle( 0 );
1511 case Okular::Settings::EnumSlidesTransition::WipeLeft
:
1513 Okular::PageTransition
transition( Okular::PageTransition::Wipe
);
1514 transition
.setAngle( 180 );
1518 case Okular::Settings::EnumSlidesTransition::WipeUp
:
1520 Okular::PageTransition
transition( Okular::PageTransition::Wipe
);
1521 transition
.setAngle( 90 );
1525 case Okular::Settings::EnumSlidesTransition::Replace
:
1527 return Okular::PageTransition( Okular::PageTransition::Replace
);
1530 // should not happen, just make gcc happy
1531 return Okular::PageTransition();
1534 /** ONLY the TRANSITIONS GENERATION function from here on **/
1535 void PresentationWidget::initTransition( const Okular::PageTransition
*transition
)
1537 // if it's just a 'replace' transition, repaint the screen
1538 if ( transition
->type() == Okular::PageTransition::Replace
)
1544 const bool isInward
= transition
->direction() == Okular::PageTransition::Inward
;
1545 const bool isHorizontal
= transition
->alignment() == Okular::PageTransition::Horizontal
;
1546 const float totalTime
= transition
->duration();
1548 m_transitionRects
.clear();
1550 switch( transition
->type() )
1552 // split: horizontal / vertical and inward / outward
1553 case Okular::PageTransition::Split
:
1555 const int steps
= isHorizontal
? 100 : 75;
1561 for ( int i
= 0; i
< steps
; i
++ )
1563 int xNext
= ((i
+ 1) * m_width
) / (2 * steps
);
1564 m_transitionRects
.push_back( QRect( xPosition
, 0, xNext
- xPosition
, m_height
) );
1565 m_transitionRects
.push_back( QRect( m_width
- xNext
, 0, xNext
- xPosition
, m_height
) );
1571 int xPosition
= m_width
/ 2;
1572 for ( int i
= 0; i
< steps
; i
++ )
1574 int xNext
= ((steps
- (i
+ 1)) * m_width
) / (2 * steps
);
1575 m_transitionRects
.push_back( QRect( xNext
, 0, xPosition
- xNext
, m_height
) );
1576 m_transitionRects
.push_back( QRect( m_width
- xPosition
, 0, xPosition
- xNext
, m_height
) );
1586 for ( int i
= 0; i
< steps
; i
++ )
1588 int yNext
= ((i
+ 1) * m_height
) / (2 * steps
);
1589 m_transitionRects
.push_back( QRect( 0, yPosition
, m_width
, yNext
- yPosition
) );
1590 m_transitionRects
.push_back( QRect( 0, m_height
- yNext
, m_width
, yNext
- yPosition
) );
1596 int yPosition
= m_height
/ 2;
1597 for ( int i
= 0; i
< steps
; i
++ )
1599 int yNext
= ((steps
- (i
+ 1)) * m_height
) / (2 * steps
);
1600 m_transitionRects
.push_back( QRect( 0, yNext
, m_width
, yPosition
- yNext
) );
1601 m_transitionRects
.push_back( QRect( 0, m_height
- yPosition
, m_width
, yPosition
- yNext
) );
1606 m_transitionMul
= 2;
1607 m_transitionDelay
= (int)( (totalTime
* 1000) / steps
);
1610 // blinds: horizontal(l-to-r) / vertical(t-to-b)
1611 case Okular::PageTransition::Blinds
:
1613 const int blinds
= isHorizontal
? 8 : 6;
1614 const int steps
= m_width
/ (4 * blinds
);
1618 for ( int b
= 0; b
< blinds
; b
++ )
1619 xPosition
[ b
] = (b
* m_width
) / blinds
;
1621 for ( int i
= 0; i
< steps
; i
++ )
1623 int stepOffset
= (int)( ((float)i
* (float)m_width
) / ((float)blinds
* (float)steps
) );
1624 for ( int b
= 0; b
< blinds
; b
++ )
1626 m_transitionRects
.push_back( QRect( xPosition
[ b
], 0, stepOffset
, m_height
) );
1627 xPosition
[ b
] = stepOffset
+ (b
* m_width
) / blinds
;
1634 for ( int b
= 0; b
< blinds
; b
++ )
1635 yPosition
[ b
] = (b
* m_height
) / blinds
;
1637 for ( int i
= 0; i
< steps
; i
++ )
1639 int stepOffset
= (int)( ((float)i
* (float)m_height
) / ((float)blinds
* (float)steps
) );
1640 for ( int b
= 0; b
< blinds
; b
++ )
1642 m_transitionRects
.push_back( QRect( 0, yPosition
[ b
], m_width
, stepOffset
) );
1643 yPosition
[ b
] = stepOffset
+ (b
* m_height
) / blinds
;
1647 m_transitionMul
= blinds
;
1648 m_transitionDelay
= (int)( (totalTime
* 1000) / steps
);
1651 // box: inward / outward
1652 case Okular::PageTransition::Box
:
1654 const int steps
= m_width
/ 10;
1657 int L
= 0, T
= 0, R
= m_width
, B
= m_height
;
1658 for ( int i
= 0; i
< steps
; i
++ )
1660 // compure shrinked box coords
1661 int newL
= ((i
+ 1) * m_width
) / (2 * steps
);
1662 int newT
= ((i
+ 1) * m_height
) / (2 * steps
);
1663 int newR
= m_width
- newL
;
1664 int newB
= m_height
- newT
;
1665 // add left, right, topcenter, bottomcenter rects
1666 m_transitionRects
.push_back( QRect( L
, T
, newL
- L
, B
- T
) );
1667 m_transitionRects
.push_back( QRect( newR
, T
, R
- newR
, B
- T
) );
1668 m_transitionRects
.push_back( QRect( newL
, T
, newR
- newL
, newT
- T
) );
1669 m_transitionRects
.push_back( QRect( newL
, newB
, newR
- newL
, B
- newB
) );
1670 L
= newL
; T
= newT
; R
= newR
, B
= newB
;
1675 int L
= m_width
/ 2, T
= m_height
/ 2, R
= L
, B
= T
;
1676 for ( int i
= 0; i
< steps
; i
++ )
1678 // compure shrinked box coords
1679 int newL
= ((steps
- (i
+ 1)) * m_width
) / (2 * steps
);
1680 int newT
= ((steps
- (i
+ 1)) * m_height
) / (2 * steps
);
1681 int newR
= m_width
- newL
;
1682 int newB
= m_height
- newT
;
1683 // add left, right, topcenter, bottomcenter rects
1684 m_transitionRects
.push_back( QRect( newL
, newT
, L
- newL
, newB
- newT
) );
1685 m_transitionRects
.push_back( QRect( R
, newT
, newR
- R
, newB
- newT
) );
1686 m_transitionRects
.push_back( QRect( L
, newT
, R
- L
, T
- newT
) );
1687 m_transitionRects
.push_back( QRect( L
, B
, R
- L
, newB
- B
) );
1688 L
= newL
; T
= newT
; R
= newR
, B
= newB
;
1691 m_transitionMul
= 4;
1692 m_transitionDelay
= (int)( (totalTime
* 1000) / steps
);
1695 // wipe: implemented for 4 canonical angles
1696 case Okular::PageTransition::Wipe
:
1698 const int angle
= transition
->angle();
1699 const int steps
= (angle
== 0) || (angle
== 180) ? m_width
/ 8 : m_height
/ 8;
1703 for ( int i
= 0; i
< steps
; i
++ )
1705 int xNext
= ((i
+ 1) * m_width
) / steps
;
1706 m_transitionRects
.push_back( QRect( xPosition
, 0, xNext
- xPosition
, m_height
) );
1710 else if ( angle
== 90 )
1712 int yPosition
= m_height
;
1713 for ( int i
= 0; i
< steps
; i
++ )
1715 int yNext
= ((steps
- (i
+ 1)) * m_height
) / steps
;
1716 m_transitionRects
.push_back( QRect( 0, yNext
, m_width
, yPosition
- yNext
) );
1720 else if ( angle
== 180 )
1722 int xPosition
= m_width
;
1723 for ( int i
= 0; i
< steps
; i
++ )
1725 int xNext
= ((steps
- (i
+ 1)) * m_width
) / steps
;
1726 m_transitionRects
.push_back( QRect( xNext
, 0, xPosition
- xNext
, m_height
) );
1730 else if ( angle
== 270 )
1733 for ( int i
= 0; i
< steps
; i
++ )
1735 int yNext
= ((i
+ 1) * m_height
) / steps
;
1736 m_transitionRects
.push_back( QRect( 0, yPosition
, m_width
, yNext
- yPosition
) );
1745 m_transitionMul
= 1;
1746 m_transitionDelay
= (int)( (totalTime
* 1000) / steps
);
1749 // dissolve: replace 'random' rects
1750 case Okular::PageTransition::Dissolve
:
1752 const int gridXsteps
= 50;
1753 const int gridYsteps
= 38;
1754 const int steps
= gridXsteps
* gridYsteps
;
1757 // create a grid of gridXstep by gridYstep QRects
1758 for ( int y
= 0; y
< gridYsteps
; y
++ )
1760 int newY
= (int)( m_height
* ((float)(y
+1) / (float)gridYsteps
) );
1761 for ( int x
= 0; x
< gridXsteps
; x
++ )
1763 int newX
= (int)( m_width
* ((float)(x
+1) / (float)gridXsteps
) );
1764 m_transitionRects
.push_back( QRect( oldX
, oldY
, newX
- oldX
, newY
- oldY
) );
1770 // randomize the grid
1771 for ( int i
= 0; i
< steps
; i
++ )
1773 int n1
= (int)(steps
* drand48());
1774 int n2
= (int)(steps
* drand48());
1775 // swap items if index differs
1778 QRect r
= m_transitionRects
[ n2
];
1779 m_transitionRects
[ n2
] = m_transitionRects
[ n1
];
1780 m_transitionRects
[ n1
] = r
;
1783 // set global transition parameters
1784 m_transitionMul
= 40;
1785 m_transitionDelay
= (int)( (m_transitionMul
* 1000 * totalTime
) / steps
);
1788 // glitter: similar to dissolve but has a direction
1789 case Okular::PageTransition::Glitter
:
1791 const int gridXsteps
= 50;
1792 const int gridYsteps
= 38;
1793 const int steps
= gridXsteps
* gridYsteps
;
1794 const int angle
= transition
->angle();
1795 // generate boxes using a given direction
1798 int yPosition
= m_height
;
1799 for ( int i
= 0; i
< gridYsteps
; i
++ )
1801 int yNext
= ((gridYsteps
- (i
+ 1)) * m_height
) / gridYsteps
;
1803 for ( int j
= 0; j
< gridXsteps
; j
++ )
1805 int xNext
= ((j
+ 1) * m_width
) / gridXsteps
;
1806 m_transitionRects
.push_back( QRect( xPosition
, yNext
, xNext
- xPosition
, yPosition
- yNext
) );
1812 else if ( angle
== 180 )
1814 int xPosition
= m_width
;
1815 for ( int i
= 0; i
< gridXsteps
; i
++ )
1817 int xNext
= ((gridXsteps
- (i
+ 1)) * m_width
) / gridXsteps
;
1819 for ( int j
= 0; j
< gridYsteps
; j
++ )
1821 int yNext
= ((j
+ 1) * m_height
) / gridYsteps
;
1822 m_transitionRects
.push_back( QRect( xNext
, yPosition
, xPosition
- xNext
, yNext
- yPosition
) );
1828 else if ( angle
== 270 )
1831 for ( int i
= 0; i
< gridYsteps
; i
++ )
1833 int yNext
= ((i
+ 1) * m_height
) / gridYsteps
;
1835 for ( int j
= 0; j
< gridXsteps
; j
++ )
1837 int xNext
= ((j
+ 1) * m_width
) / gridXsteps
;
1838 m_transitionRects
.push_back( QRect( xPosition
, yPosition
, xNext
- xPosition
, yNext
- yPosition
) );
1844 else // if angle is 0 or 315
1847 for ( int i
= 0; i
< gridXsteps
; i
++ )
1849 int xNext
= ((i
+ 1) * m_width
) / gridXsteps
;
1851 for ( int j
= 0; j
< gridYsteps
; j
++ )
1853 int yNext
= ((j
+ 1) * m_height
) / gridYsteps
;
1854 m_transitionRects
.push_back( QRect( xPosition
, yPosition
, xNext
- xPosition
, yNext
- yPosition
) );
1860 // add a 'glitter' (1 over 10 pieces is randomized)
1861 int randomSteps
= steps
/ 20;
1862 for ( int i
= 0; i
< randomSteps
; i
++ )
1864 int n1
= (int)(steps
* drand48());
1865 int n2
= (int)(steps
* drand48());
1866 // swap items if index differs
1869 QRect r
= m_transitionRects
[ n2
];
1870 m_transitionRects
[ n2
] = m_transitionRects
[ n1
];
1871 m_transitionRects
[ n1
] = r
;
1874 // set global transition parameters
1875 m_transitionMul
= (angle
== 90) || (angle
== 270) ? gridYsteps
: gridXsteps
;
1876 m_transitionMul
/= 2;
1877 m_transitionDelay
= (int)( (m_transitionMul
* 1000 * totalTime
) / steps
);
1880 // implement missing transitions (a binary raster engine needed here)
1881 case Okular::PageTransition::Fly
:
1883 case Okular::PageTransition::Push
:
1885 case Okular::PageTransition::Cover
:
1887 case Okular::PageTransition::Uncover
:
1889 case Okular::PageTransition::Fade
:
1896 // send the first start to the timer
1897 m_transitionTimer
->start( 0 );
1901 #include "presentationwidget.moc"