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
)
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_document( doc
), m_frameIndex( -1 ), m_topBar( 0 ), m_pagesEdit( 0 ), m_searchBar( 0 ),
131 m_screenSelect( 0 ), m_blockNotifications( false ), m_inBlackScreenMode( false )
134 setAttribute( Qt::WA_DeleteOnClose
);
135 setAttribute( Qt::WA_OpaquePaintEvent
);
136 setObjectName( "presentationWidget" );
137 QString caption
= doc
->metaData( "DocumentTitle" ).toString();
138 if ( caption
.trimmed().isEmpty() )
139 caption
= doc
->currentDocument().fileName();
140 setWindowTitle( KDialog::makeStandardCaption( caption
) );
145 // create top toolbar
146 m_topBar
= new PresentationToolBar( this );
147 m_topBar
->setObjectName( "presentationBar" );
148 m_topBar
->setIconSize( QSize( 32, 32 ) );
149 m_topBar
->setMovable( false );
150 m_topBar
->addAction( KIcon( layoutDirection() == Qt::RightToLeft
? "go-next" : "go-previous" ), i18n( "Previous Page" ), this, SLOT( slotPrevPage() ) );
151 m_pagesEdit
= new KLineEdit( m_topBar
);
152 QSizePolicy sp
= m_pagesEdit
->sizePolicy();
153 sp
.setHorizontalPolicy( QSizePolicy::Minimum
);
154 m_pagesEdit
->setSizePolicy( sp
);
155 QFontMetrics
fm( m_pagesEdit
->font() );
156 QStyleOptionFrame option
;
157 option
.initFrom( m_pagesEdit
);
158 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
159 QIntValidator
*validator
= new QIntValidator( 1, m_document
->pages(), m_pagesEdit
);
160 m_pagesEdit
->setValidator( validator
);
161 m_topBar
->addWidget( m_pagesEdit
);
162 QLabel
*pagesLabel
= new QLabel( m_topBar
);
163 pagesLabel
->setText( QLatin1String( " / " ) + QString::number( m_document
->pages() ) + QLatin1String( " " ) );
164 m_topBar
->addWidget( pagesLabel
);
165 connect( m_pagesEdit
, SIGNAL( returnPressed() ), this, SLOT( slotPageChanged() ) );
166 m_topBar
->addAction( KIcon( layoutDirection() == Qt::RightToLeft
? "go-previous" : "go-next" ), i18n( "Next Page" ), this, SLOT( slotNextPage() ) );
167 m_topBar
->addSeparator();
168 QAction
* drawingAct
= m_topBar
->addAction( KIcon( "draw-freehand" ), i18n( "Toggle Drawing Mode" ) );
169 drawingAct
->setCheckable( true );
170 connect( drawingAct
, SIGNAL( toggled( bool ) ), this, SLOT( togglePencilMode( bool ) ) );
171 QAction
* eraseDrawingAct
= m_topBar
->addAction( KIcon( "draw-eraser" ), i18n( "Erase Drawings" ) );
172 connect( eraseDrawingAct
, SIGNAL( triggered() ), this, SLOT( clearDrawings() ) );
173 QDesktopWidget
*desktop
= QApplication::desktop();
174 if ( desktop
->numScreens() > 1 )
176 m_topBar
->addSeparator();
177 m_screenSelect
= new KSelectAction( KIcon( "video-display" ), i18n( "Switch Screen" ), m_topBar
);
178 m_screenSelect
->setToolBarMode( KSelectAction::MenuMode
);
179 m_screenSelect
->setToolButtonPopupMode( QToolButton::InstantPopup
);
180 m_topBar
->addAction( m_screenSelect
);
181 const int screenCount
= desktop
->numScreens();
182 for ( int i
= 0; i
< screenCount
; ++i
)
184 QAction
*act
= m_screenSelect
->addAction( i18nc( "%1 is the screen number (0, 1, ...)", "Screen %1", i
) );
185 act
->setData( qVariantFromValue( i
) );
188 QWidget
*spacer
= new QWidget( m_topBar
);
189 spacer
->setSizePolicy( QSizePolicy::Expanding
, QSizePolicy::MinimumExpanding
);
190 m_topBar
->addWidget( spacer
);
191 m_topBar
->addAction( KIcon( "application-exit" ), i18n( "Exit Presentation Mode" ), this, SLOT( close() ) );
192 m_topBar
->setAutoFillBackground( true );
194 // change topbar background color
195 QPalette p
= m_topBar
->palette();
196 p
.setColor( QPalette::Active
, QPalette::Button
, Qt::gray
);
197 p
.setColor( QPalette::Active
, QPalette::Background
, Qt::darkGray
);
198 m_topBar
->setPalette( p
);
201 setMouseTracking( true );
202 setContextMenuPolicy( Qt::PreventContextMenu
);
203 m_transitionTimer
= new QTimer( this );
204 m_transitionTimer
->setSingleShot( true );
205 connect( m_transitionTimer
, SIGNAL( timeout() ), this, SLOT( slotTransitionStep() ) );
206 m_overlayHideTimer
= new QTimer( this );
207 m_overlayHideTimer
->setSingleShot( true );
208 connect( m_overlayHideTimer
, SIGNAL( timeout() ), this, SLOT( slotHideOverlay() ) );
209 m_nextPageTimer
= new QTimer( this );
210 m_nextPageTimer
->setSingleShot( true );
211 connect( m_nextPageTimer
, SIGNAL( timeout() ), this, SLOT( slotNextPage() ) );
213 // handle cursor appearance as specified in configuration
214 if ( Okular::Settings::slidesCursor() == Okular::Settings::EnumSlidesCursor::HiddenDelay
)
216 KCursor::setAutoHideCursor( this, true );
217 KCursor::setHideCursorDelay( 3000 );
219 else if ( Okular::Settings::slidesCursor() == Okular::Settings::EnumSlidesCursor::Hidden
)
221 setCursor( QCursor( Qt::BlankCursor
) );
224 // inhibit the screen saver
225 inhibitScreenSaver();
227 QTimer::singleShot( 0, this, SLOT( slotDelayedEvents() ) );
230 PresentationWidget::~PresentationWidget()
232 // allow the screen saver again
235 // stop the audio playbacks
236 Okular::AudioPlayer::instance()->stopPlaybacks();
238 // remove our highlights
241 m_document
->resetSearch( PRESENTATION_SEARCH_ID
);
244 // remove this widget from document observer
245 m_document
->removeObserver( this );
247 m_document
->removePageAnnotations( m_document
->viewport().pageNumber
, m_currentPageDrawings
);
248 delete m_drawingEngine
;
251 QVector
< PresentationFrame
* >::iterator fIt
= m_frames
.begin(), fEnd
= m_frames
.end();
252 for ( ; fIt
!= fEnd
; ++fIt
)
257 void PresentationWidget::notifySetup( const QVector
< Okular::Page
* > & pageSet
, int setupFlags
)
259 // same document, nothing to change - here we assume the document sets up
260 // us with the whole document set as first notifySetup()
261 if ( !( setupFlags
& Okular::DocumentObserver::DocumentChanged
) )
264 // delete previous frames (if any (shouldn't be))
265 QVector
< PresentationFrame
* >::iterator fIt
= m_frames
.begin(), fEnd
= m_frames
.end();
266 for ( ; fIt
!= fEnd
; ++fIt
)
268 if ( !m_frames
.isEmpty() )
269 kWarning() << "Frames setup changed while a Presentation is in progress.";
272 // create the new frames
273 QVector
< Okular::Page
* >::const_iterator setIt
= pageSet
.begin(), setEnd
= pageSet
.end();
274 float screenRatio
= (float)m_height
/ (float)m_width
;
275 for ( ; setIt
!= setEnd
; ++setIt
)
277 PresentationFrame
* frame
= new PresentationFrame();
278 frame
->page
= *setIt
;
279 const QLinkedList
< Okular::Annotation
* > annotations
= (*setIt
)->annotations();
280 QLinkedList
< Okular::Annotation
* >::const_iterator aIt
= annotations
.begin(), aEnd
= annotations
.end();
281 for ( ; aIt
!= aEnd
; ++aIt
)
283 Okular::Annotation
* a
= *aIt
;
284 if ( a
->subType() == Okular::Annotation::AMovie
)
286 Okular::MovieAnnotation
* movieAnn
= static_cast< Okular::MovieAnnotation
* >( a
);
287 VideoWidget
* vw
= new VideoWidget( movieAnn
, m_document
, this );
288 frame
->videoWidgets
.insert( movieAnn
->movie(), vw
);
292 frame
->recalcGeometry( m_width
, m_height
, screenRatio
);
293 // add the frame to the vector
294 m_frames
.push_back( frame
);
297 // get metadata from the document
298 m_metaStrings
.clear();
299 const Okular::DocumentInfo
* info
= m_document
->documentInfo();
302 if ( !info
->get( "title" ).isNull() )
303 m_metaStrings
+= i18n( "Title: %1", info
->get( "title" ) );
304 if ( !info
->get( "author" ).isNull() )
305 m_metaStrings
+= i18n( "Author: %1", info
->get( "author" ) );
307 m_metaStrings
+= i18n( "Pages: %1", m_document
->pages() );
308 m_metaStrings
+= i18n( "Click to begin" );
311 void PresentationWidget::notifyViewportChanged( bool /*smoothMove*/ )
313 // discard notifications if displaying the summary
314 if ( m_frameIndex
== -1 && Okular::Settings::slidesShowSummary() )
317 // display the current page
318 changePage( m_document
->viewport().pageNumber
);
320 // auto advance to the next page if set
321 startAutoChangeTimer();
324 void PresentationWidget::notifyPageChanged( int pageNumber
, int changedFlags
)
326 // if we are blocking the notifications, do nothing
327 if ( m_blockNotifications
)
330 // check if it's the last requested pixmap. if so update the widget.
331 if ( (changedFlags
& ( DocumentObserver::Pixmap
| DocumentObserver::Annotations
| DocumentObserver::Highlights
) ) && pageNumber
== m_frameIndex
)
332 generatePage( changedFlags
& ( DocumentObserver::Annotations
| DocumentObserver::Highlights
) );
335 bool PresentationWidget::canUnloadPixmap( int pageNumber
) const
337 if ( Okular::Settings::memoryLevel() != Okular::Settings::EnumMemoryLevel::Aggressive
)
339 // can unload all pixmaps except for the currently visible one
340 return pageNumber
!= m_frameIndex
;
344 // can unload all pixmaps except for the currently visible one, previous and next
345 return qAbs(pageNumber
- m_frameIndex
) <= 1;
349 void PresentationWidget::setupActions( KActionCollection
* collection
)
352 addAction( m_ac
->action( "go_previous" ) );
353 addAction( m_ac
->action( "go_next" ) );
354 addAction( m_ac
->action( "first_page" ) );
355 addAction( m_ac
->action( "last_page" ) );
357 QAction
*action
= m_ac
->action( "switch_blackscreen_mode" );
358 connect( action
, SIGNAL( toggled( bool ) ), SLOT( toggleBlackScreenMode( bool ) ) );
364 bool PresentationWidget::event( QEvent
* e
)
366 if ( e
->type() == QEvent::ToolTip
)
368 QHelpEvent
* he
= (QHelpEvent
*)e
;
371 const Okular::Action
* link
= getLink( he
->x(), he
->y(), &r
);
375 QString tip
= link
->actionTip();
376 if ( !tip
.isEmpty() )
377 QToolTip::showText( he
->globalPos(), tip
, this, r
);
383 // do not stop the event
384 return QWidget::event( e
);
387 void PresentationWidget::keyPressEvent( QKeyEvent
* e
)
389 if (m_width
== -1) return;
391 if ( e
->key() == Qt::Key_Left
|| e
->key() == Qt::Key_Backspace
|| e
->key() == Qt::Key_PageUp
)
393 else if ( e
->key() == Qt::Key_Right
|| e
->key() == Qt::Key_Space
|| e
->key() == Qt::Key_PageDown
)
395 else if ( e
->key() == Qt::Key_Home
)
397 else if ( e
->key() == Qt::Key_End
)
399 else if ( e
->key() == Qt::Key_Escape
)
401 if ( !m_topBar
->isHidden() )
408 void PresentationWidget::wheelEvent( QWheelEvent
* e
)
410 // performance note: don't remove the clipping
411 int div
= e
->delta() / 120;
428 void PresentationWidget::mousePressEvent( QMouseEvent
* e
)
430 if ( m_drawingEngine
)
432 QRect r
= routeMouseDrawingEvent( e
);
435 m_drawingRect
|= r
.translated( m_frames
[ m_frameIndex
]->geometry
.topLeft() );
436 update( m_drawingRect
);
441 // pressing left button
442 if ( e
->button() == Qt::LeftButton
)
444 // if pressing on a link, skip other checks
445 if ( ( m_pressedLink
= getLink( e
->x(), e
->y() ) ) )
448 // handle clicking on top-right overlay
449 if ( m_overlayGeometry
.contains( e
->pos() ) )
451 overlayClick( e
->pos() );
455 // if no other actions, go to next page
458 // pressing right button
459 else if ( e
->button() == Qt::RightButton
)
463 void PresentationWidget::mouseReleaseEvent( QMouseEvent
* e
)
465 if ( m_drawingEngine
)
467 QRect r
= routeMouseDrawingEvent( e
);
469 if ( m_drawingEngine
->creationCompleted() )
471 QList
< Okular::Annotation
* > annots
= m_drawingEngine
->end();
472 // manually disable and re-enable the pencil mode, so we can do
473 // cleaning of the actual drawer and create a new one just after
474 // that - that gives continuous drawing
475 togglePencilMode( false );
476 togglePencilMode( true );
477 foreach( Okular::Annotation
* ann
, annots
)
478 m_document
->addPageAnnotation( m_frameIndex
, ann
);
479 m_currentPageDrawings
<< annots
;
484 // if releasing on the same link we pressed over, execute it
485 if ( m_pressedLink
&& e
->button() == Qt::LeftButton
)
487 const Okular::Action
* link
= getLink( e
->x(), e
->y() );
488 if ( link
== m_pressedLink
)
489 m_document
->processAction( link
);
494 void PresentationWidget::mouseMoveEvent( QMouseEvent
* e
)
500 // update cursor and tooltip if hovering a link
501 if ( !m_drawingEngine
&& Okular::Settings::slidesCursor() != Okular::Settings::EnumSlidesCursor::Hidden
)
502 testCursorOnLink( e
->x(), e
->y() );
504 if ( !m_topBar
->isHidden() )
506 // hide a shown bar when exiting the area
507 if ( e
->y() > ( m_topBar
->height() + 1 ) )
510 setFocus( Qt::OtherFocusReason
);
515 if ( m_drawingEngine
&& e
->buttons() != Qt::NoButton
)
517 QRect r
= routeMouseDrawingEvent( e
);
519 m_drawingRect
|= r
.translated( m_frames
[ m_frameIndex
]->geometry
.topLeft() );
520 update( m_drawingRect
);
525 // show the bar if reaching top 2 pixels
528 // handle "dragging the wheel" if clicking on its geometry
529 else if ( ( QApplication::mouseButtons() & Qt::LeftButton
) && m_overlayGeometry
.contains( e
->pos() ) )
530 overlayClick( e
->pos() );
535 void PresentationWidget::paintEvent( QPaintEvent
* pe
)
537 if ( m_inBlackScreenMode
)
539 QPainter
painter( this );
540 painter
.fillRect( pe
->rect(), Qt::black
);
549 connect( m_document
, SIGNAL( linkFind() ), this, SLOT( slotFind() ) );
551 // register this observer in document. events will come immediately
552 m_document
->addObserver( this );
554 // show summary if requested
555 if ( Okular::Settings::slidesShowSummary() )
559 // check painting rect consistancy
560 QRect r
= pe
->rect().intersect( QRect( QPoint( 0, 0 ), geometry().size() ) );
564 if ( m_lastRenderedPixmap
.isNull() )
566 QPainter
painter( this );
567 painter
.fillRect( pe
->rect(), Okular::Settings::slidesBackgroundColor() );
571 // blit the pixmap to the screen
572 QVector
<QRect
> allRects
= pe
->region().rects();
573 uint numRects
= allRects
.count();
574 QPainter
painter( this );
575 for ( uint i
= 0; i
< numRects
; i
++ )
577 const QRect
& r
= allRects
[i
];
580 #ifdef ENABLE_PROGRESS_OVERLAY
581 if ( Okular::Settings::slidesShowProgress() && r
.intersects( m_overlayGeometry
) )
583 // backbuffer the overlay operation
584 QPixmap
backPixmap( r
.size() );
585 QPainter
pixPainter( &backPixmap
);
587 // first draw the background on the backbuffer
588 pixPainter
.drawPixmap( QPoint(0,0), m_lastRenderedPixmap
, r
);
590 // then blend the overlay (a piece of) over the background
591 QRect ovr
= m_overlayGeometry
.intersect( r
);
592 pixPainter
.drawPixmap( ovr
.left() - r
.left(), ovr
.top() - r
.top(),
593 m_lastRenderedOverlay
, ovr
.left() - m_overlayGeometry
.left(),
594 ovr
.top() - m_overlayGeometry
.top(), ovr
.width(), ovr
.height() );
596 // finally blit the pixmap to the screen
598 painter
.drawPixmap( r
.topLeft(), backPixmap
, backPixmap
.rect() );
601 // copy the rendered pixmap to the screen
602 painter
.drawPixmap( r
.topLeft(), m_lastRenderedPixmap
, r
);
604 if ( m_drawingEngine
&& m_drawingRect
.intersects( pe
->rect() ) )
606 const QRect
& geom
= m_frames
[ m_frameIndex
]->geometry
;
608 painter
.translate( geom
.topLeft() );
609 m_drawingEngine
->paint( &painter
, geom
.width(), geom
.height(), m_drawingRect
.intersect( pe
->rect() ) );
617 const Okular::Action
* PresentationWidget::getLink( int x
, int y
, QRect
* geometry
) const
619 // no links on invalid pages
620 if ( geometry
&& !geometry
->isNull() )
621 geometry
->setRect( 0, 0, 0, 0 );
622 if ( m_frameIndex
< 0 || m_frameIndex
>= (int)m_frames
.size() )
625 // get frame, page and geometry
626 const PresentationFrame
* frame
= m_frames
[ m_frameIndex
];
627 const Okular::Page
* page
= frame
->page
;
628 const QRect
& frameGeometry
= frame
->geometry
;
630 // compute normalized x and y
631 double nx
= (double)(x
- frameGeometry
.left()) / (double)frameGeometry
.width();
632 double ny
= (double)(y
- frameGeometry
.top()) / (double)frameGeometry
.height();
634 // no links outside the pages
635 if ( nx
< 0 || nx
> 1 || ny
< 0 || ny
> 1 )
638 // check if 1) there is an object and 2) it's a link
639 const QRect d
= QApplication::desktop()->screenGeometry( m_screen
);
640 const Okular::ObjectRect
* object
= page
->objectRect( Okular::ObjectRect::Action
, nx
, ny
, d
.width(), d
.height() );
644 // compute link geometry if destination rect present
647 *geometry
= object
->boundingRect( frameGeometry
.width(), frameGeometry
.height() );
648 geometry
->translate( frameGeometry
.left(), frameGeometry
.top() );
651 // return the link pointer
652 return (Okular::Action
*)object
->object();
655 void PresentationWidget::testCursorOnLink( int x
, int y
)
657 const Okular::Action
* link
= getLink( x
, y
, 0 );
659 // only react on changes (in/out from a link)
660 if ( (link
&& !m_handCursor
) || (!link
&& m_handCursor
) )
662 // change cursor shape
663 m_handCursor
= link
!= 0;
664 setCursor( QCursor( m_handCursor
? Qt::PointingHandCursor
: Qt::ArrowCursor
) );
668 void PresentationWidget::overlayClick( const QPoint
& position
)
670 // clicking the progress indicator
671 int xPos
= position
.x() - m_overlayGeometry
.x() - m_overlayGeometry
.width() / 2,
672 yPos
= m_overlayGeometry
.height() / 2 - position
.y();
673 if ( !xPos
&& !yPos
)
676 // compute angle relative to indicator (note coord transformation)
677 float angle
= 0.5 + 0.5 * atan2( (double)-xPos
, (double)-yPos
) / M_PI
;
678 int pageIndex
= (int)( angle
* ( m_frames
.count() - 1 ) + 0.5 );
680 // go to selected page
681 changePage( pageIndex
);
684 void PresentationWidget::changePage( int newPage
)
686 if ( m_frameIndex
== newPage
)
689 const int oldIndex
= m_frameIndex
;
690 // check if pixmap exists or else request it
691 m_frameIndex
= newPage
;
692 PresentationFrame
* frame
= m_frames
[ m_frameIndex
];
693 int pixW
= frame
->geometry
.width();
694 int pixH
= frame
->geometry
.height();
696 bool signalsBlocked
= m_pagesEdit
->signalsBlocked();
697 m_pagesEdit
->blockSignals( true );
698 m_pagesEdit
->setText( QString::number( m_frameIndex
+ 1 ) );
699 m_pagesEdit
->blockSignals( signalsBlocked
);
701 // if pixmap not inside the Okular::Page we request it and wait for
702 // notifyPixmapChanged call or else we can proceed to pixmap generation
703 if ( !frame
->page
->hasPixmap( PRESENTATION_ID
, pixW
, pixH
) )
709 // make the background pixmap
713 // set a new viewport in document if page number differs
714 if ( m_frameIndex
!= -1 && m_frameIndex
!= m_document
->viewport().pageNumber
)
716 // stop the audio playback, if any
717 Okular::AudioPlayer::instance()->stopPlaybacks();
718 // perform the page closing action, if any
719 if ( m_document
->page( m_document
->viewport().pageNumber
)->pageAction( Okular::Page::Closing
) )
720 m_document
->processAction( m_document
->page( m_document
->viewport().pageNumber
)->pageAction( Okular::Page::Closing
) );
722 // remove the drawing on the old page before switching
724 m_document
->setViewportPage( m_frameIndex
, PRESENTATION_ID
);
726 // perform the page opening action, if any
727 if ( m_document
->page( m_frameIndex
)->pageAction( Okular::Page::Opening
) )
728 m_document
->processAction( m_document
->page( m_frameIndex
)->pageAction( Okular::Page::Opening
) );
732 if ( oldIndex
!= m_frameIndex
)
734 if ( oldIndex
!= -1 )
736 Q_FOREACH ( VideoWidget
*vw
, m_frames
[ oldIndex
]->videoWidgets
)
743 Q_FOREACH ( VideoWidget
*vw
, m_frames
[ m_frameIndex
]->videoWidgets
)
752 void PresentationWidget::generatePage( bool disableTransition
)
754 if ( m_lastRenderedPixmap
.isNull() )
755 m_lastRenderedPixmap
= QPixmap( m_width
, m_height
);
757 // opens the painter over the pixmap
758 QPainter pixmapPainter
;
759 pixmapPainter
.begin( &m_lastRenderedPixmap
);
760 // generate welcome page
761 if ( m_frameIndex
== -1 )
762 generateIntroPage( pixmapPainter
);
763 // generate a normal pixmap with extended margin filling
764 if ( m_frameIndex
>= 0 && m_frameIndex
< (int)m_document
->pages() )
765 generateContentsPage( m_frameIndex
, pixmapPainter
);
768 // generate the top-right corner overlay
769 #ifdef ENABLE_PROGRESS_OVERLAY
770 if ( Okular::Settings::slidesShowProgress() && m_frameIndex
!= -1 )
774 // start transition on pages that have one
775 if ( !disableTransition
&& Okular::Settings::slidesTransitionsEnabled() )
777 const Okular::PageTransition
* transition
= m_frameIndex
!= -1 ?
778 m_frames
[ m_frameIndex
]->page
->transition() : 0;
780 initTransition( transition
);
782 Okular::PageTransition trans
= defaultTransition();
783 initTransition( &trans
);
788 Okular::PageTransition trans
= defaultTransition( Okular::Settings::EnumSlidesTransition::Replace
);
789 initTransition( &trans
);
792 // update cursor + tooltip
793 if ( Okular::Settings::slidesCursor() != Okular::Settings::EnumSlidesCursor::Hidden
)
795 QPoint p
= mapFromGlobal( QCursor::pos() );
796 testCursorOnLink( p
.x(), p
.y() );
800 void PresentationWidget::generateIntroPage( QPainter
& p
)
802 // use a vertical gray gradient background
803 int blend1
= m_height
/ 10,
804 blend2
= 9 * m_height
/ 10;
805 int baseTint
= QColor(Qt::gray
).red();
806 for ( int i
= 0; i
< m_height
; i
++ )
810 k
-= (int)( baseTint
* (i
-blend1
)*(i
-blend1
) / (float)(blend1
* blend1
) );
812 k
+= (int)( (255-baseTint
) * (i
-blend2
)*(i
-blend2
) / (float)(blend1
* blend1
) );
813 p
.fillRect( 0, i
, m_width
, 1, QColor( k
, k
, k
) );
816 // draw okular logo in the four corners
817 QPixmap logo
= DesktopIcon( "okular", 64 );
818 if ( !logo
.isNull() )
820 p
.drawPixmap( 5, 5, logo
);
821 p
.drawPixmap( m_width
- 5 - logo
.width(), 5, logo
);
822 p
.drawPixmap( m_width
- 5 - logo
.width(), m_height
- 5 - logo
.height(), logo
);
823 p
.drawPixmap( 5, m_height
- 5 - logo
.height(), logo
);
826 // draw metadata text (the last line is 'click to begin')
827 int strNum
= m_metaStrings
.count(),
828 strHeight
= m_height
/ ( strNum
+ 4 ),
829 fontHeight
= 2 * strHeight
/ 3;
830 QFont
font( p
.font() );
831 font
.setPixelSize( fontHeight
);
832 QFontMetrics
metrics( font
);
833 for ( int i
= 0; i
< strNum
; i
++ )
835 // set a font to fit text width
836 float wScale
= (float)metrics
.boundingRect( m_metaStrings
[i
] ).width() / (float)m_width
;
839 f
.setPixelSize( (int)( (float)fontHeight
/ (float)wScale
) );
843 p
.setPen( Qt::darkGray
);
844 p
.drawText( 2, m_height
/ 4 + strHeight
* i
+ 2, m_width
, strHeight
,
845 Qt::AlignHCenter
| Qt::AlignVCenter
, m_metaStrings
[i
] );
847 p
.setPen( 128 + (127 * i
) / strNum
);
848 p
.drawText( 0, m_height
/ 4 + strHeight
* i
, m_width
, strHeight
,
849 Qt::AlignHCenter
| Qt::AlignVCenter
, m_metaStrings
[i
] );
853 void PresentationWidget::generateContentsPage( int pageNum
, QPainter
& p
)
855 PresentationFrame
* frame
= m_frames
[ pageNum
];
857 // translate painter and contents rect
858 QRect
geom( frame
->geometry
);
859 p
.translate( geom
.left(), geom
.top() );
860 geom
.translate( -geom
.left(), -geom
.top() );
862 // draw the page using the shared PagePainter class
863 int flags
= PagePainter::Accessibility
| PagePainter::Highlights
| PagePainter::Annotations
;
864 PagePainter::paintPageOnPainter( &p
, frame
->page
, PRESENTATION_ID
, flags
,
865 geom
.width(), geom
.height(), geom
);
868 p
.translate( -frame
->geometry
.left(), -frame
->geometry
.top() );
870 // fill unpainted areas with background color
871 QRegion
unpainted( QRect( 0, 0, m_width
, m_height
) );
872 QVector
<QRect
> rects
= unpainted
.subtract( frame
->geometry
).rects();
873 for ( int i
= 0; i
< rects
.count(); i
++ )
875 const QRect
& r
= rects
[i
];
876 p
.fillRect( r
, Okular::Settings::slidesBackgroundColor() );
880 // from Arthur - Qt4 - (is defined elsewhere as 'qt_div_255' to not break final compilation)
881 inline int qt_div255(int x
) { return (x
+ (x
>>8) + 0x80) >> 8; }
882 void PresentationWidget::generateOverlay()
884 #ifdef ENABLE_PROGRESS_OVERLAY
885 // calculate overlay geometry and resize pixmap if needed
886 int side
= m_width
/ 16;
887 m_overlayGeometry
.setRect( m_width
- side
- 4, 4, side
, side
);
889 // note: to get a sort of antialiasing, we render the pixmap double sized
890 // and the resulting image is smoothly scaled down. So here we open a
891 // painter on the double sized pixmap.
893 QPixmap
doublePixmap( side
, side
);
894 doublePixmap
.fill( Qt::black
);
895 QPainter
pixmapPainter( &doublePixmap
);
896 pixmapPainter
.setRenderHints( QPainter::Antialiasing
);
898 // draw PIE SLICES in blue levels (the levels will then be the alpha component)
899 int pages
= m_document
->pages();
901 { // draw continuous slices
902 int degrees
= (int)( 360 * (float)(m_frameIndex
+ 1) / (float)pages
);
903 pixmapPainter
.setPen( 0x05 );
904 pixmapPainter
.setBrush( QColor( 0x40 ) );
905 pixmapPainter
.drawPie( 2, 2, side
- 4, side
- 4, 90*16, (360-degrees
)*16 );
906 pixmapPainter
.setPen( 0x40 );
907 pixmapPainter
.setBrush( QColor( 0xF0 ) );
908 pixmapPainter
.drawPie( 2, 2, side
- 4, side
- 4, 90*16, -degrees
*16 );
911 { // draw discrete slices
912 float oldCoord
= -90;
913 for ( int i
= 0; i
< pages
; i
++ )
915 float newCoord
= -90 + 360 * (float)(i
+ 1) / (float)pages
;
916 pixmapPainter
.setPen( i
<= m_frameIndex
? 0x40 : 0x05 );
917 pixmapPainter
.setBrush( QColor( i
<= m_frameIndex
? 0xF0 : 0x40 ) );
918 pixmapPainter
.drawPie( 2, 2, side
- 4, side
- 4,
919 (int)( -16*(oldCoord
+ 1) ), (int)( -16*(newCoord
- (oldCoord
+ 2)) ) );
923 int circleOut
= side
/ 4;
924 pixmapPainter
.setPen( Qt::black
);
925 pixmapPainter
.setBrush( Qt::black
);
926 pixmapPainter
.drawEllipse( circleOut
, circleOut
, side
- 2*circleOut
, side
- 2*circleOut
);
928 // draw TEXT using maximum opacity
929 QFont
f( pixmapPainter
.font() );
930 f
.setPixelSize( side
/ 4 );
931 pixmapPainter
.setFont( f
);
932 pixmapPainter
.setPen( 0xFF );
933 // use a little offset to prettify output
934 pixmapPainter
.drawText( 2, 2, side
, side
, Qt::AlignCenter
, QString::number( m_frameIndex
+ 1 ) );
936 // end drawing pixmap and halve image
938 QImage
image( doublePixmap
.toImage().scaled( side
/ 2, side
/ 2, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
) );
939 image
= image
.convertToFormat( QImage::Format_ARGB32
);
941 // draw circular shadow using the same technique
942 doublePixmap
.fill( Qt::black
);
943 pixmapPainter
.begin( &doublePixmap
);
944 pixmapPainter
.setPen( 0x40 );
945 pixmapPainter
.setBrush( QColor( 0x80 ) );
946 pixmapPainter
.drawEllipse( 0, 0, side
, side
);
948 QImage
shadow( doublePixmap
.toImage().scaled( side
/ 2, side
/ 2, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
) );
950 // generate a 2 colors pixmap using mixing shadow (made with highlight color)
951 // and image (made with highlightedText color)
952 QPalette pal
= palette();
953 QColor color
= pal
.color( QPalette::Active
, QPalette::HighlightedText
);
954 int red
= color
.red(), green
= color
.green(), blue
= color
.blue();
955 color
= pal
.color( QPalette::Active
, QPalette::Highlight
);
956 int sRed
= color
.red(), sGreen
= color
.green(), sBlue
= color
.blue();
958 unsigned int * data
= (unsigned int *)image
.bits(),
959 * shadowData
= (unsigned int *)shadow
.bits(),
960 pixels
= image
.width() * image
.height();
961 // cache data (reduce computation time to 26%!)
962 int c1
= -1, c2
= -1, cR
= 0, cG
= 0, cB
= 0, cA
= 0;
964 for( unsigned int i
= 0; i
< pixels
; ++i
)
966 // alpha for shadow and image
967 int shadowAlpha
= shadowData
[i
] & 0xFF,
968 srcAlpha
= data
[i
] & 0xFF;
970 if ( srcAlpha
!= c1
|| shadowAlpha
!= c2
)
974 // fuse color components and alpha value of image over shadow
976 cR
= qt_div255( srcAlpha
* red
+ (255 - srcAlpha
) * sRed
),
977 cG
= qt_div255( srcAlpha
* green
+ (255 - srcAlpha
) * sGreen
),
978 cB
= qt_div255( srcAlpha
* blue
+ (255 - srcAlpha
) * sBlue
),
979 cA
= qt_div255( srcAlpha
* srcAlpha
+ (255 - srcAlpha
) * shadowAlpha
)
983 data
[i
] = qRgba( cR
, cG
, cB
, cA
);
985 m_lastRenderedOverlay
= QPixmap::fromImage( image
);
987 // start the autohide timer
988 //repaint( m_overlayGeometry ); // toggle with next line
989 update( m_overlayGeometry
);
990 m_overlayHideTimer
->start( 2500 );
995 QRect
PresentationWidget::routeMouseDrawingEvent( QMouseEvent
* e
)
997 const QRect
& geom
= m_frames
[ m_frameIndex
]->geometry
;
998 const Okular::Page
* page
= m_frames
[ m_frameIndex
]->page
;
1000 AnnotatorEngine::EventType eventType
= AnnotatorEngine::Press
;
1001 if ( e
->type() == QEvent::MouseMove
)
1002 eventType
= AnnotatorEngine::Move
;
1003 else if ( e
->type() == QEvent::MouseButtonRelease
)
1004 eventType
= AnnotatorEngine::Release
;
1006 // find out the pressed button
1007 AnnotatorEngine::Button button
= AnnotatorEngine::None
;
1008 Qt::MouseButtons buttonState
= ( eventType
== AnnotatorEngine::Move
) ? e
->buttons() : e
->button();
1009 if ( buttonState
== Qt::LeftButton
)
1010 button
= AnnotatorEngine::Left
;
1011 else if ( buttonState
== Qt::RightButton
)
1012 button
= AnnotatorEngine::Right
;
1013 static bool hasclicked
= false;
1014 if ( eventType
== AnnotatorEngine::Press
)
1017 double nX
= ( (double)e
->x() - (double)geom
.left() ) / (double)geom
.width();
1018 double nY
= ( (double)e
->y() - (double)geom
.top() ) / (double)geom
.height();
1020 if ( hasclicked
&& nX
>= 0 && nX
< 1 && nY
>= 0 && nY
< 1 )
1021 ret
= m_drawingEngine
->event( eventType
, button
, nX
, nY
, geom
.width(), geom
.height(), page
);
1023 if ( eventType
== AnnotatorEngine::Release
)
1029 void PresentationWidget::startAutoChangeTimer()
1031 double pageDuration
= m_frameIndex
>= 0 && m_frameIndex
< (int)m_frames
.count() ? m_frames
[ m_frameIndex
]->page
->duration() : -1;
1032 if ( Okular::Settings::slidesAdvance() || pageDuration
>= 0.0 )
1034 double secs
= pageDuration
< 0.0
1035 ? Okular::Settings::slidesAdvanceTime()
1036 : qMin
<double>( pageDuration
, Okular::Settings::slidesAdvanceTime() );
1037 m_nextPageTimer
->start( (int)( secs
* 1000 ) );
1041 void PresentationWidget::recalcGeometry()
1043 QDesktopWidget
*desktop
= QApplication::desktop();
1044 const int preferenceScreen
= Okular::Settings::slidesScreen();
1046 if ( preferenceScreen
== -2 )
1048 screen
= desktop
->screenNumber( parentWidget() );
1050 else if ( preferenceScreen
== -1 )
1052 screen
= desktop
->primaryScreen();
1054 else if ( preferenceScreen
>= 0 && preferenceScreen
< desktop
->numScreens() )
1056 screen
= preferenceScreen
;
1060 screen
= desktop
->screenNumber( parentWidget() );
1061 Okular::Settings::setSlidesScreen( -2 );
1063 const QRect screenGeom
= desktop
->screenGeometry( screen
);
1064 // kDebug() << screen << "=>" << screenGeom;
1066 setGeometry( screenGeom
);
1069 void PresentationWidget::repositionContent()
1071 const QRect ourGeom
= geometry();
1073 m_topBar
->setGeometry( 0, 0, ourGeom
.width(), 32 + 10 );
1076 void PresentationWidget::requestPixmaps()
1078 PresentationFrame
* frame
= m_frames
[ m_frameIndex
];
1079 int pixW
= frame
->geometry
.width();
1080 int pixH
= frame
->geometry
.height();
1082 // operation will take long: set busy cursor
1083 QApplication::setOverrideCursor( QCursor( Qt::BusyCursor
) );
1084 // request the pixmap
1085 QLinkedList
< Okular::PixmapRequest
* > requests
;
1086 requests
.push_back( new Okular::PixmapRequest( PRESENTATION_ID
, m_frameIndex
, pixW
, pixH
, PRESENTATION_PRIO
, false ) );
1088 QApplication::restoreOverrideCursor();
1089 // ask for next and previous page if not in low memory usage setting
1090 if ( Okular::Settings::memoryLevel() != Okular::Settings::EnumMemoryLevel::Low
&& Okular::Settings::enableThreading() )
1092 if ( m_frameIndex
+ 1 < (int)m_document
->pages() )
1094 PresentationFrame
*nextFrame
= m_frames
[ m_frameIndex
+ 1 ];
1095 pixW
= nextFrame
->geometry
.width();
1096 pixH
= nextFrame
->geometry
.height();
1097 if ( !nextFrame
->page
->hasPixmap( PRESENTATION_ID
, pixW
, pixH
) )
1098 requests
.push_back( new Okular::PixmapRequest( PRESENTATION_ID
, m_frameIndex
+ 1, pixW
, pixH
, PRESENTATION_PRELOAD_PRIO
, true ) );
1100 if ( m_frameIndex
- 1 >= 0 )
1102 PresentationFrame
*prevFrame
= m_frames
[ m_frameIndex
- 1 ];
1103 pixW
= prevFrame
->geometry
.width();
1104 pixH
= prevFrame
->geometry
.height();
1105 if ( !prevFrame
->page
->hasPixmap( PRESENTATION_ID
, pixW
, pixH
) )
1106 requests
.push_back( new Okular::PixmapRequest( PRESENTATION_ID
, m_frameIndex
- 1, pixW
, pixH
, PRESENTATION_PRELOAD_PRIO
, true ) );
1109 m_document
->requestPixmaps( requests
);
1113 void PresentationWidget::slotNextPage()
1115 // loop when configured
1116 if ( m_frameIndex
== (int)m_frames
.count() - 1 && Okular::Settings::slidesLoop() )
1119 if ( m_frameIndex
< (int)m_frames
.count() - 1 )
1122 changePage( m_frameIndex
+ 1 );
1123 // auto advance to the next page if set
1124 startAutoChangeTimer();
1128 #ifdef ENABLE_PROGRESS_OVERLAY
1129 if ( Okular::Settings::slidesShowProgress() )
1132 if ( m_transitionTimer
->isActive() )
1134 m_transitionTimer
->stop();
1138 // we need the setFocus() call here to let KCursor::autoHide() work correctly
1142 void PresentationWidget::slotPrevPage()
1144 if ( m_frameIndex
> 0 )
1146 // go to previous page
1147 changePage( m_frameIndex
- 1 );
1149 // auto advance to the next page if set
1150 startAutoChangeTimer();
1154 #ifdef ENABLE_PROGRESS_OVERLAY
1155 if ( Okular::Settings::slidesShowProgress() )
1158 if ( m_transitionTimer
->isActive() )
1160 m_transitionTimer
->stop();
1166 void PresentationWidget::slotFirstPage()
1171 void PresentationWidget::slotLastPage()
1173 changePage( (int)m_frames
.count() - 1 );
1176 void PresentationWidget::slotHideOverlay()
1178 QRect
geom( m_overlayGeometry
);
1179 m_overlayGeometry
.setCoords( 0, 0, -1, -1 );
1183 void PresentationWidget::slotTransitionStep()
1185 if ( m_transitionRects
.empty() )
1187 // it's better to fix the transition to cover the whole screen than
1188 // enabling the following line that wastes cpu for nothing
1193 for ( int i
= 0; i
< m_transitionMul
&& !m_transitionRects
.empty(); i
++ )
1195 update( m_transitionRects
.first() );
1196 m_transitionRects
.pop_front();
1198 m_transitionTimer
->start( m_transitionDelay
);
1201 void PresentationWidget::slotDelayedEvents()
1204 repositionContent();
1206 if ( m_screenSelect
)
1208 m_screenSelect
->setCurrentItem( m_screen
);
1209 connect( m_screenSelect
->selectableActionGroup(), SIGNAL( triggered( QAction
* ) ),
1210 this, SLOT( chooseScreen( QAction
* ) ) );
1213 // show widget and take control
1215 setWindowState( windowState() | Qt::WindowFullScreen
);
1217 connect( QApplication::desktop(), SIGNAL( resized( int ) ), this, SLOT( screenResized( int ) ) );
1219 // inform user on how to exit from presentation mode
1220 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" );
1223 void PresentationWidget::slotPageChanged()
1226 int p
= m_pagesEdit
->text().toInt( &ok
);
1230 changePage( p
- 1 );
1233 void PresentationWidget::togglePencilMode( bool on
)
1237 QString colorstring
= Okular::Settings::slidesPencilColor().name();
1238 // FIXME this should not be recreated every time
1239 QDomDocument
doc( "engine" );
1240 QDomElement root
= doc
.createElement( "engine" );
1241 root
.setAttribute( "color", colorstring
);
1242 doc
.appendChild( root
);
1243 QDomElement annElem
= doc
.createElement( "annotation" );
1244 root
.appendChild( annElem
);
1245 annElem
.setAttribute( "type", "Ink" );
1246 annElem
.setAttribute( "color", colorstring
);
1247 annElem
.setAttribute( "width", "2" );
1248 m_drawingEngine
= new SmoothPathEngine( root
);
1249 setCursor( KCursor( "pencil", Qt::ArrowCursor
) );
1253 delete m_drawingEngine
;
1254 m_drawingEngine
= 0;
1255 m_drawingRect
= QRect();
1256 setCursor( Qt::ArrowCursor
);
1260 void PresentationWidget::clearDrawings()
1262 m_document
->removePageAnnotations( m_document
->viewport().pageNumber
, m_currentPageDrawings
);
1263 m_currentPageDrawings
.clear();
1266 void PresentationWidget::screenResized( int screen
)
1268 // we can ignore if a screen was resized in the case the screen is not
1270 if ( screen
!= m_screen
)
1273 setScreen( screen
);
1276 void PresentationWidget::chooseScreen( QAction
*act
)
1278 if ( !act
|| act
->data().type() != QVariant::Int
)
1281 const int newScreen
= act
->data().toInt();
1283 setScreen( newScreen
);
1286 void PresentationWidget::toggleBlackScreenMode( bool )
1288 m_inBlackScreenMode
= !m_inBlackScreenMode
;
1293 void PresentationWidget::setScreen( int newScreen
)
1295 const QRect screenGeom
= QApplication::desktop()->screenGeometry( newScreen
);
1296 const QSize oldSize
= size();
1297 // kDebug() << newScreen << "=>" << screenGeom;
1298 m_screen
= newScreen
;
1299 setGeometry( screenGeom
);
1301 repositionContent();
1303 // if by chance the new screen has the same resolution of the previous,
1304 // do not invalidate pixmaps and such..
1305 if ( size() == oldSize
)
1309 m_height
= height();
1311 // update the frames
1312 QVector
< PresentationFrame
* >::const_iterator fIt
= m_frames
.begin(), fEnd
= m_frames
.end();
1313 const float screenRatio
= (float)m_height
/ (float)m_width
;
1314 for ( ; fIt
!= fEnd
; ++fIt
)
1316 (*fIt
)->recalcGeometry( m_width
, m_height
, screenRatio
);
1320 const_cast< Okular::Page
* >( m_frames
[ m_frameIndex
]->page
)->deletePixmap( PRESENTATION_ID
);
1321 // force the regeneration of the pixmap
1322 m_lastRenderedPixmap
= QPixmap();
1323 m_blockNotifications
= true;
1325 m_blockNotifications
= false;
1326 generatePage( true /* no transitions */ );
1329 void PresentationWidget::inhibitScreenSaver()
1331 QDBusMessage message
= QDBusMessage::createMethodCall( "org.freedesktop.ScreenSaver", "/ScreenSaver",
1332 "org.freedesktop.ScreenSaver", "Inhibit" );
1333 message
<< QString( "Okular" );
1334 message
<< i18n( "Giving a presentation" );
1336 QDBusReply
<uint
> reply
= QDBusConnection::sessionBus().call( message
);
1337 if ( reply
.isValid() )
1338 m_screenSaverCookie
= reply
.value();
1341 void PresentationWidget::allowScreenSaver()
1343 if ( m_screenSaverCookie
!= -1 )
1345 QDBusMessage message
= QDBusMessage::createMethodCall( "org.freedesktop.ScreenSaver", "/ScreenSaver",
1346 "org.freedesktop.ScreenSaver", "UnInhibit" );
1347 message
<< (uint
)m_screenSaverCookie
;
1348 QDBusConnection::sessionBus().send( message
);
1352 void PresentationWidget::slotFind()
1356 m_searchBar
= new PresentationSearchBar( m_document
, this, this );
1357 m_searchBar
->forceSnap();
1359 m_searchBar
->focusOnSearchEdit();
1360 m_searchBar
->show();
1364 const Okular::PageTransition
PresentationWidget::defaultTransition() const
1366 return defaultTransition( Okular::Settings::slidesTransition() );
1369 const Okular::PageTransition
PresentationWidget::defaultTransition( int type
) const
1373 case Okular::Settings::EnumSlidesTransition::BlindsHorizontal
:
1375 Okular::PageTransition
transition( Okular::PageTransition::Blinds
);
1376 transition
.setAlignment( Okular::PageTransition::Horizontal
);
1380 case Okular::Settings::EnumSlidesTransition::BlindsVertical
:
1382 Okular::PageTransition
transition( Okular::PageTransition::Blinds
);
1383 transition
.setAlignment( Okular::PageTransition::Vertical
);
1387 case Okular::Settings::EnumSlidesTransition::BoxIn
:
1389 Okular::PageTransition
transition( Okular::PageTransition::Box
);
1390 transition
.setDirection( Okular::PageTransition::Inward
);
1394 case Okular::Settings::EnumSlidesTransition::BoxOut
:
1396 Okular::PageTransition
transition( Okular::PageTransition::Box
);
1397 transition
.setDirection( Okular::PageTransition::Outward
);
1401 case Okular::Settings::EnumSlidesTransition::Dissolve
:
1403 return Okular::PageTransition( Okular::PageTransition::Dissolve
);
1406 case Okular::Settings::EnumSlidesTransition::GlitterDown
:
1408 Okular::PageTransition
transition( Okular::PageTransition::Glitter
);
1409 transition
.setAngle( 270 );
1413 case Okular::Settings::EnumSlidesTransition::GlitterRight
:
1415 Okular::PageTransition
transition( Okular::PageTransition::Glitter
);
1416 transition
.setAngle( 0 );
1420 case Okular::Settings::EnumSlidesTransition::GlitterRightDown
:
1422 Okular::PageTransition
transition( Okular::PageTransition::Glitter
);
1423 transition
.setAngle( 315 );
1427 case Okular::Settings::EnumSlidesTransition::Random
:
1429 return defaultTransition( KRandom::random() % 18 );
1432 case Okular::Settings::EnumSlidesTransition::SplitHorizontalIn
:
1434 Okular::PageTransition
transition( Okular::PageTransition::Split
);
1435 transition
.setAlignment( Okular::PageTransition::Horizontal
);
1436 transition
.setDirection( Okular::PageTransition::Inward
);
1440 case Okular::Settings::EnumSlidesTransition::SplitHorizontalOut
:
1442 Okular::PageTransition
transition( Okular::PageTransition::Split
);
1443 transition
.setAlignment( Okular::PageTransition::Horizontal
);
1444 transition
.setDirection( Okular::PageTransition::Outward
);
1448 case Okular::Settings::EnumSlidesTransition::SplitVerticalIn
:
1450 Okular::PageTransition
transition( Okular::PageTransition::Split
);
1451 transition
.setAlignment( Okular::PageTransition::Vertical
);
1452 transition
.setDirection( Okular::PageTransition::Inward
);
1456 case Okular::Settings::EnumSlidesTransition::SplitVerticalOut
:
1458 Okular::PageTransition
transition( Okular::PageTransition::Split
);
1459 transition
.setAlignment( Okular::PageTransition::Vertical
);
1460 transition
.setDirection( Okular::PageTransition::Outward
);
1464 case Okular::Settings::EnumSlidesTransition::WipeDown
:
1466 Okular::PageTransition
transition( Okular::PageTransition::Wipe
);
1467 transition
.setAngle( 270 );
1471 case Okular::Settings::EnumSlidesTransition::WipeRight
:
1473 Okular::PageTransition
transition( Okular::PageTransition::Wipe
);
1474 transition
.setAngle( 0 );
1478 case Okular::Settings::EnumSlidesTransition::WipeLeft
:
1480 Okular::PageTransition
transition( Okular::PageTransition::Wipe
);
1481 transition
.setAngle( 180 );
1485 case Okular::Settings::EnumSlidesTransition::WipeUp
:
1487 Okular::PageTransition
transition( Okular::PageTransition::Wipe
);
1488 transition
.setAngle( 90 );
1492 case Okular::Settings::EnumSlidesTransition::Replace
:
1494 return Okular::PageTransition( Okular::PageTransition::Replace
);
1497 // should not happen, just make gcc happy
1498 return Okular::PageTransition();
1501 /** ONLY the TRANSITIONS GENERATION function from here on **/
1502 void PresentationWidget::initTransition( const Okular::PageTransition
*transition
)
1504 // if it's just a 'replace' transition, repaint the screen
1505 if ( transition
->type() == Okular::PageTransition::Replace
)
1511 const bool isInward
= transition
->direction() == Okular::PageTransition::Inward
;
1512 const bool isHorizontal
= transition
->alignment() == Okular::PageTransition::Horizontal
;
1513 const float totalTime
= transition
->duration();
1515 m_transitionRects
.clear();
1517 switch( transition
->type() )
1519 // split: horizontal / vertical and inward / outward
1520 case Okular::PageTransition::Split
:
1522 const int steps
= isHorizontal
? 100 : 75;
1528 for ( int i
= 0; i
< steps
; i
++ )
1530 int xNext
= ((i
+ 1) * m_width
) / (2 * steps
);
1531 m_transitionRects
.push_back( QRect( xPosition
, 0, xNext
- xPosition
, m_height
) );
1532 m_transitionRects
.push_back( QRect( m_width
- xNext
, 0, xNext
- xPosition
, m_height
) );
1538 int xPosition
= m_width
/ 2;
1539 for ( int i
= 0; i
< steps
; i
++ )
1541 int xNext
= ((steps
- (i
+ 1)) * m_width
) / (2 * steps
);
1542 m_transitionRects
.push_back( QRect( xNext
, 0, xPosition
- xNext
, m_height
) );
1543 m_transitionRects
.push_back( QRect( m_width
- xPosition
, 0, xPosition
- xNext
, m_height
) );
1553 for ( int i
= 0; i
< steps
; i
++ )
1555 int yNext
= ((i
+ 1) * m_height
) / (2 * steps
);
1556 m_transitionRects
.push_back( QRect( 0, yPosition
, m_width
, yNext
- yPosition
) );
1557 m_transitionRects
.push_back( QRect( 0, m_height
- yNext
, m_width
, yNext
- yPosition
) );
1563 int yPosition
= m_height
/ 2;
1564 for ( int i
= 0; i
< steps
; i
++ )
1566 int yNext
= ((steps
- (i
+ 1)) * m_height
) / (2 * steps
);
1567 m_transitionRects
.push_back( QRect( 0, yNext
, m_width
, yPosition
- yNext
) );
1568 m_transitionRects
.push_back( QRect( 0, m_height
- yPosition
, m_width
, yPosition
- yNext
) );
1573 m_transitionMul
= 2;
1574 m_transitionDelay
= (int)( (totalTime
* 1000) / steps
);
1577 // blinds: horizontal(l-to-r) / vertical(t-to-b)
1578 case Okular::PageTransition::Blinds
:
1580 const int blinds
= isHorizontal
? 8 : 6;
1581 const int steps
= m_width
/ (4 * blinds
);
1585 for ( int b
= 0; b
< blinds
; b
++ )
1586 xPosition
[ b
] = (b
* m_width
) / blinds
;
1588 for ( int i
= 0; i
< steps
; i
++ )
1590 int stepOffset
= (int)( ((float)i
* (float)m_width
) / ((float)blinds
* (float)steps
) );
1591 for ( int b
= 0; b
< blinds
; b
++ )
1593 m_transitionRects
.push_back( QRect( xPosition
[ b
], 0, stepOffset
, m_height
) );
1594 xPosition
[ b
] = stepOffset
+ (b
* m_width
) / blinds
;
1601 for ( int b
= 0; b
< blinds
; b
++ )
1602 yPosition
[ b
] = (b
* m_height
) / blinds
;
1604 for ( int i
= 0; i
< steps
; i
++ )
1606 int stepOffset
= (int)( ((float)i
* (float)m_height
) / ((float)blinds
* (float)steps
) );
1607 for ( int b
= 0; b
< blinds
; b
++ )
1609 m_transitionRects
.push_back( QRect( 0, yPosition
[ b
], m_width
, stepOffset
) );
1610 yPosition
[ b
] = stepOffset
+ (b
* m_height
) / blinds
;
1614 m_transitionMul
= blinds
;
1615 m_transitionDelay
= (int)( (totalTime
* 1000) / steps
);
1618 // box: inward / outward
1619 case Okular::PageTransition::Box
:
1621 const int steps
= m_width
/ 10;
1624 int L
= 0, T
= 0, R
= m_width
, B
= m_height
;
1625 for ( int i
= 0; i
< steps
; i
++ )
1627 // compure shrinked box coords
1628 int newL
= ((i
+ 1) * m_width
) / (2 * steps
);
1629 int newT
= ((i
+ 1) * m_height
) / (2 * steps
);
1630 int newR
= m_width
- newL
;
1631 int newB
= m_height
- newT
;
1632 // add left, right, topcenter, bottomcenter rects
1633 m_transitionRects
.push_back( QRect( L
, T
, newL
- L
, B
- T
) );
1634 m_transitionRects
.push_back( QRect( newR
, T
, R
- newR
, B
- T
) );
1635 m_transitionRects
.push_back( QRect( newL
, T
, newR
- newL
, newT
- T
) );
1636 m_transitionRects
.push_back( QRect( newL
, newB
, newR
- newL
, B
- newB
) );
1637 L
= newL
; T
= newT
; R
= newR
, B
= newB
;
1642 int L
= m_width
/ 2, T
= m_height
/ 2, R
= L
, B
= T
;
1643 for ( int i
= 0; i
< steps
; i
++ )
1645 // compure shrinked box coords
1646 int newL
= ((steps
- (i
+ 1)) * m_width
) / (2 * steps
);
1647 int newT
= ((steps
- (i
+ 1)) * m_height
) / (2 * steps
);
1648 int newR
= m_width
- newL
;
1649 int newB
= m_height
- newT
;
1650 // add left, right, topcenter, bottomcenter rects
1651 m_transitionRects
.push_back( QRect( newL
, newT
, L
- newL
, newB
- newT
) );
1652 m_transitionRects
.push_back( QRect( R
, newT
, newR
- R
, newB
- newT
) );
1653 m_transitionRects
.push_back( QRect( L
, newT
, R
- L
, T
- newT
) );
1654 m_transitionRects
.push_back( QRect( L
, B
, R
- L
, newB
- B
) );
1655 L
= newL
; T
= newT
; R
= newR
, B
= newB
;
1658 m_transitionMul
= 4;
1659 m_transitionDelay
= (int)( (totalTime
* 1000) / steps
);
1662 // wipe: implemented for 4 canonical angles
1663 case Okular::PageTransition::Wipe
:
1665 const int angle
= transition
->angle();
1666 const int steps
= (angle
== 0) || (angle
== 180) ? m_width
/ 8 : m_height
/ 8;
1670 for ( int i
= 0; i
< steps
; i
++ )
1672 int xNext
= ((i
+ 1) * m_width
) / steps
;
1673 m_transitionRects
.push_back( QRect( xPosition
, 0, xNext
- xPosition
, m_height
) );
1677 else if ( angle
== 90 )
1679 int yPosition
= m_height
;
1680 for ( int i
= 0; i
< steps
; i
++ )
1682 int yNext
= ((steps
- (i
+ 1)) * m_height
) / steps
;
1683 m_transitionRects
.push_back( QRect( 0, yNext
, m_width
, yPosition
- yNext
) );
1687 else if ( angle
== 180 )
1689 int xPosition
= m_width
;
1690 for ( int i
= 0; i
< steps
; i
++ )
1692 int xNext
= ((steps
- (i
+ 1)) * m_width
) / steps
;
1693 m_transitionRects
.push_back( QRect( xNext
, 0, xPosition
- xNext
, m_height
) );
1697 else if ( angle
== 270 )
1700 for ( int i
= 0; i
< steps
; i
++ )
1702 int yNext
= ((i
+ 1) * m_height
) / steps
;
1703 m_transitionRects
.push_back( QRect( 0, yPosition
, m_width
, yNext
- yPosition
) );
1712 m_transitionMul
= 1;
1713 m_transitionDelay
= (int)( (totalTime
* 1000) / steps
);
1716 // dissolve: replace 'random' rects
1717 case Okular::PageTransition::Dissolve
:
1719 const int gridXsteps
= 50;
1720 const int gridYsteps
= 38;
1721 const int steps
= gridXsteps
* gridYsteps
;
1724 // create a grid of gridXstep by gridYstep QRects
1725 for ( int y
= 0; y
< gridYsteps
; y
++ )
1727 int newY
= (int)( m_height
* ((float)(y
+1) / (float)gridYsteps
) );
1728 for ( int x
= 0; x
< gridXsteps
; x
++ )
1730 int newX
= (int)( m_width
* ((float)(x
+1) / (float)gridXsteps
) );
1731 m_transitionRects
.push_back( QRect( oldX
, oldY
, newX
- oldX
, newY
- oldY
) );
1737 // randomize the grid
1738 for ( int i
= 0; i
< steps
; i
++ )
1740 int n1
= (int)(steps
* drand48());
1741 int n2
= (int)(steps
* drand48());
1742 // swap items if index differs
1745 QRect r
= m_transitionRects
[ n2
];
1746 m_transitionRects
[ n2
] = m_transitionRects
[ n1
];
1747 m_transitionRects
[ n1
] = r
;
1750 // set global transition parameters
1751 m_transitionMul
= 40;
1752 m_transitionDelay
= (int)( (m_transitionMul
* 1000 * totalTime
) / steps
);
1755 // glitter: similar to dissolve but has a direction
1756 case Okular::PageTransition::Glitter
:
1758 const int gridXsteps
= 50;
1759 const int gridYsteps
= 38;
1760 const int steps
= gridXsteps
* gridYsteps
;
1761 const int angle
= transition
->angle();
1762 // generate boxes using a given direction
1765 int yPosition
= m_height
;
1766 for ( int i
= 0; i
< gridYsteps
; i
++ )
1768 int yNext
= ((gridYsteps
- (i
+ 1)) * m_height
) / gridYsteps
;
1770 for ( int j
= 0; j
< gridXsteps
; j
++ )
1772 int xNext
= ((j
+ 1) * m_width
) / gridXsteps
;
1773 m_transitionRects
.push_back( QRect( xPosition
, yNext
, xNext
- xPosition
, yPosition
- yNext
) );
1779 else if ( angle
== 180 )
1781 int xPosition
= m_width
;
1782 for ( int i
= 0; i
< gridXsteps
; i
++ )
1784 int xNext
= ((gridXsteps
- (i
+ 1)) * m_width
) / gridXsteps
;
1786 for ( int j
= 0; j
< gridYsteps
; j
++ )
1788 int yNext
= ((j
+ 1) * m_height
) / gridYsteps
;
1789 m_transitionRects
.push_back( QRect( xNext
, yPosition
, xPosition
- xNext
, yNext
- yPosition
) );
1795 else if ( angle
== 270 )
1798 for ( int i
= 0; i
< gridYsteps
; i
++ )
1800 int yNext
= ((i
+ 1) * m_height
) / gridYsteps
;
1802 for ( int j
= 0; j
< gridXsteps
; j
++ )
1804 int xNext
= ((j
+ 1) * m_width
) / gridXsteps
;
1805 m_transitionRects
.push_back( QRect( xPosition
, yPosition
, xNext
- xPosition
, yNext
- yPosition
) );
1811 else // if angle is 0 or 315
1814 for ( int i
= 0; i
< gridXsteps
; i
++ )
1816 int xNext
= ((i
+ 1) * m_width
) / gridXsteps
;
1818 for ( int j
= 0; j
< gridYsteps
; j
++ )
1820 int yNext
= ((j
+ 1) * m_height
) / gridYsteps
;
1821 m_transitionRects
.push_back( QRect( xPosition
, yPosition
, xNext
- xPosition
, yNext
- yPosition
) );
1827 // add a 'glitter' (1 over 10 pieces is randomized)
1828 int randomSteps
= steps
/ 20;
1829 for ( int i
= 0; i
< randomSteps
; i
++ )
1831 int n1
= (int)(steps
* drand48());
1832 int n2
= (int)(steps
* drand48());
1833 // swap items if index differs
1836 QRect r
= m_transitionRects
[ n2
];
1837 m_transitionRects
[ n2
] = m_transitionRects
[ n1
];
1838 m_transitionRects
[ n1
] = r
;
1841 // set global transition parameters
1842 m_transitionMul
= (angle
== 90) || (angle
== 270) ? gridYsteps
: gridXsteps
;
1843 m_transitionMul
/= 2;
1844 m_transitionDelay
= (int)( (m_transitionMul
* 1000 * totalTime
) / steps
);
1847 // implement missing transitions (a binary raster engine needed here)
1848 case Okular::PageTransition::Fly
:
1850 case Okular::PageTransition::Push
:
1852 case Okular::PageTransition::Cover
:
1854 case Okular::PageTransition::Uncover
:
1856 case Okular::PageTransition::Fade
:
1863 // send the first start to the timer
1864 m_transitionTimer
->start( 0 );
1868 #include "presentationwidget.moc"