mirror of
https://invent.kde.org/graphics/okular
synced 2024-08-24 18:35:37 +00:00
![Simone Gaiarin](/assets/img/avatar_default.png)
It is more logical to have the drawing tools first, then the eraser, and then the action to delete everything. Also the two actions to erase things are close to each other now. The icon of "erase drawing" has been changed to draw-eraser-delete-objects to distinguish it from the "Eraser" tool. BUG: 399734 Differential Revision: https://phabricator.kde.org/D16171
2459 lines
88 KiB
C++
2459 lines
88 KiB
C++
/***************************************************************************
|
||
* Copyright (C) 2004 by Enrico Ros <eros.kde@email.it> *
|
||
* *
|
||
* This program is free software; you can redistribute it and/or modify *
|
||
* it under the terms of the GNU General Public License as published by *
|
||
* the Free Software Foundation; either version 2 of the License, or *
|
||
* (at your option) any later version. *
|
||
***************************************************************************/
|
||
|
||
#include "presentationwidget.h"
|
||
|
||
// qt/kde includes
|
||
#include <qloggingcategory.h>
|
||
#include <QDBusConnection>
|
||
#include <QDBusMessage>
|
||
#include <QDBusReply>
|
||
|
||
#include <qevent.h>
|
||
#include <qfontmetrics.h>
|
||
#include <QIcon>
|
||
#include <qtimer.h>
|
||
#include <qimage.h>
|
||
#include <qlabel.h>
|
||
#include <qlayout.h>
|
||
#include <qpainter.h>
|
||
#include <qstyle.h>
|
||
#include <qstyleoption.h>
|
||
#include <qtooltip.h>
|
||
#include <qvalidator.h>
|
||
#include <qapplication.h>
|
||
#include <qdesktopwidget.h>
|
||
#include <QGestureEvent>
|
||
#include <kcursor.h>
|
||
#include <krandom.h>
|
||
#include <qtoolbar.h>
|
||
#include <qaction.h>
|
||
#include <kactioncollection.h>
|
||
#include <klineedit.h>
|
||
#include <KLocalizedString>
|
||
#include <kiconloader.h>
|
||
#include <kmessagebox.h>
|
||
#include <kselectaction.h>
|
||
#include <QDialog>
|
||
|
||
#ifdef Q_OS_LINUX
|
||
#include <QDBusUnixFileDescriptor>
|
||
#include <unistd.h> // For ::close() for sleep inhibition
|
||
#endif
|
||
|
||
// system includes
|
||
#include <stdlib.h>
|
||
#include <math.h>
|
||
|
||
// local includes
|
||
#include "annotationtools.h"
|
||
#include "debug_ui.h"
|
||
#include "drawingtoolactions.h"
|
||
#include "guiutils.h"
|
||
#include "pagepainter.h"
|
||
#include "presentationsearchbar.h"
|
||
#include "priorities.h"
|
||
#include "videowidget.h"
|
||
#include "core/action.h"
|
||
#include "core/annotations.h"
|
||
#include "core/audioplayer.h"
|
||
#include "core/document.h"
|
||
#include "core/generator.h"
|
||
#include "core/movie.h"
|
||
#include "core/page.h"
|
||
#include "settings.h"
|
||
#include "settings_core.h"
|
||
|
||
// comment this to disable the top-right progress indicator
|
||
#define ENABLE_PROGRESS_OVERLAY
|
||
|
||
|
||
// a frame contains a pointer to the page object, its geometry and the
|
||
// transition effect to the next frame
|
||
struct PresentationFrame
|
||
{
|
||
~PresentationFrame()
|
||
{
|
||
qDeleteAll( videoWidgets );
|
||
}
|
||
|
||
void recalcGeometry( int width, int height, float screenRatio )
|
||
{
|
||
// calculate frame geometry keeping constant aspect ratio
|
||
float pageRatio = page->ratio();
|
||
int pageWidth = width,
|
||
pageHeight = height;
|
||
if ( pageRatio > screenRatio )
|
||
pageWidth = (int)( (float)pageHeight / pageRatio );
|
||
else
|
||
pageHeight = (int)( (float)pageWidth * pageRatio );
|
||
geometry.setRect( ( width - pageWidth ) / 2,
|
||
( height - pageHeight ) / 2,
|
||
pageWidth, pageHeight );
|
||
|
||
Q_FOREACH ( VideoWidget *vw, videoWidgets )
|
||
{
|
||
const Okular::NormalizedRect r = vw->normGeometry();
|
||
QRect vwgeom = r.geometry( geometry.width(), geometry.height() );
|
||
vw->resize( vwgeom.size() );
|
||
vw->move( geometry.topLeft() + vwgeom.topLeft() );
|
||
}
|
||
}
|
||
|
||
const Okular::Page * page;
|
||
QRect geometry;
|
||
QHash< Okular::Movie *, VideoWidget * > videoWidgets;
|
||
QLinkedList< SmoothPath > drawings;
|
||
};
|
||
|
||
|
||
// a custom QToolBar that basically does not propagate the event if the widget
|
||
// background is not automatically filled
|
||
class PresentationToolBar : public QToolBar
|
||
{
|
||
Q_OBJECT
|
||
|
||
public:
|
||
PresentationToolBar( QWidget * parent = Q_NULLPTR )
|
||
: QToolBar( parent )
|
||
{}
|
||
|
||
protected:
|
||
void mousePressEvent( QMouseEvent * e ) override
|
||
{
|
||
QToolBar::mousePressEvent( e );
|
||
e->accept();
|
||
}
|
||
|
||
void mouseReleaseEvent( QMouseEvent * e ) override
|
||
{
|
||
QToolBar::mouseReleaseEvent( e );
|
||
e->accept();
|
||
}
|
||
};
|
||
|
||
|
||
PresentationWidget::PresentationWidget( QWidget * parent, Okular::Document * doc, DrawingToolActions * drawingToolActions, KActionCollection * collection )
|
||
: QWidget( nullptr /* must be null, to have an independent widget */, Qt::FramelessWindowHint ),
|
||
m_pressedLink( nullptr ), m_handCursor( false ), m_drawingEngine( nullptr ),
|
||
m_screenInhibitCookie(0), m_sleepInhibitFd(-1),
|
||
m_parentWidget( parent ),
|
||
m_document( doc ), m_frameIndex( -1 ), m_topBar( nullptr ), m_pagesEdit( nullptr ), m_searchBar( nullptr ),
|
||
m_ac( collection ), m_screenSelect( nullptr ), m_isSetup( false ), m_blockNotifications( false ), m_inBlackScreenMode( false ),
|
||
m_showSummaryView( Okular::Settings::slidesShowSummary() ),
|
||
m_advanceSlides( Okular::SettingsCore::slidesAdvance() ),
|
||
m_goToNextPageOnRelease( false )
|
||
{
|
||
Q_UNUSED( parent )
|
||
setAttribute( Qt::WA_DeleteOnClose );
|
||
setAttribute( Qt::WA_OpaquePaintEvent );
|
||
setObjectName( QStringLiteral( "presentationWidget" ) );
|
||
QString caption = doc->metaData( QStringLiteral("DocumentTitle") ).toString();
|
||
if ( caption.trimmed().isEmpty() )
|
||
caption = doc->currentDocument().fileName();
|
||
caption = i18nc( "[document title/filename] – Presentation", "%1 – Presentation", caption );
|
||
setWindowTitle( caption );
|
||
|
||
m_width = -1;
|
||
m_screen = -2;
|
||
|
||
// create top toolbar
|
||
m_topBar = new PresentationToolBar( this );
|
||
m_topBar->setObjectName( QStringLiteral( "presentationBar" ) );
|
||
m_topBar->setMovable( false );
|
||
m_topBar->layout()->setMargin(0);
|
||
m_topBar->addAction( QIcon::fromTheme( layoutDirection() == Qt::RightToLeft ? QStringLiteral("go-next") : QStringLiteral("go-previous") ), i18n( "Previous Page" ), this, SLOT(slotPrevPage()) );
|
||
m_pagesEdit = new KLineEdit( m_topBar );
|
||
QSizePolicy sp = m_pagesEdit->sizePolicy();
|
||
sp.setHorizontalPolicy( QSizePolicy::Minimum );
|
||
m_pagesEdit->setSizePolicy( sp );
|
||
QFontMetrics fm( m_pagesEdit->font() );
|
||
QStyleOptionFrame option;
|
||
option.initFrom( m_pagesEdit );
|
||
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
|
||
QIntValidator *validator = new QIntValidator( 1, m_document->pages(), m_pagesEdit );
|
||
m_pagesEdit->setValidator( validator );
|
||
m_topBar->addWidget( m_pagesEdit );
|
||
QLabel *pagesLabel = new QLabel( m_topBar );
|
||
pagesLabel->setText( QLatin1String( " / " ) + QString::number( m_document->pages() ) + QLatin1String( " " ) );
|
||
m_topBar->addWidget( pagesLabel );
|
||
connect(m_pagesEdit, &QLineEdit::returnPressed, this, &PresentationWidget::slotPageChanged);
|
||
m_topBar->addAction( QIcon::fromTheme( layoutDirection() == Qt::RightToLeft ? QStringLiteral("go-previous") : QStringLiteral("go-next") ), i18n( "Next Page" ), this, SLOT(slotNextPage()) );
|
||
m_topBar->addSeparator();
|
||
QAction *playPauseAct = collection->action( QStringLiteral("presentation_play_pause") );
|
||
playPauseAct->setEnabled( true );
|
||
connect(playPauseAct, &QAction::triggered, this, &PresentationWidget::slotTogglePlayPause);
|
||
m_topBar->addAction( playPauseAct );
|
||
setPlayPauseIcon();
|
||
addAction( playPauseAct );
|
||
m_topBar->addSeparator();
|
||
|
||
foreach(QAction *action, drawingToolActions->actions())
|
||
{
|
||
action->setEnabled( true );
|
||
m_topBar->addAction( action );
|
||
addAction( action );
|
||
}
|
||
connect( drawingToolActions, &DrawingToolActions::changeEngine, this, &PresentationWidget::slotChangeDrawingToolEngine );
|
||
connect( drawingToolActions, &DrawingToolActions::actionsRecreated, this, &PresentationWidget::slotAddDrawingToolActions );
|
||
|
||
QAction *eraseDrawingAct = collection->action( QStringLiteral("presentation_erase_drawings") );
|
||
eraseDrawingAct->setEnabled( true );
|
||
connect(eraseDrawingAct, &QAction::triggered, this, &PresentationWidget::clearDrawings);
|
||
m_topBar->addAction( eraseDrawingAct );
|
||
addAction( eraseDrawingAct );
|
||
|
||
QDesktopWidget *desktop = QApplication::desktop();
|
||
if ( desktop->numScreens() > 1 )
|
||
{
|
||
m_topBar->addSeparator();
|
||
m_screenSelect = new KSelectAction( QIcon::fromTheme( QStringLiteral("video-display") ), i18n( "Switch Screen" ), m_topBar );
|
||
m_screenSelect->setToolBarMode( KSelectAction::MenuMode );
|
||
m_screenSelect->setToolButtonPopupMode( QToolButton::InstantPopup );
|
||
m_topBar->addAction( m_screenSelect );
|
||
const int screenCount = desktop->numScreens();
|
||
for ( int i = 0; i < screenCount; ++i )
|
||
{
|
||
QAction *act = m_screenSelect->addAction( i18nc( "%1 is the screen number (0, 1, ...)", "Screen %1", i ) );
|
||
act->setData( qVariantFromValue( i ) );
|
||
}
|
||
}
|
||
QWidget *spacer = new QWidget( m_topBar );
|
||
spacer->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::MinimumExpanding );
|
||
m_topBar->addWidget( spacer );
|
||
m_topBar->addAction( QIcon::fromTheme( QStringLiteral("application-exit") ), i18n( "Exit Presentation Mode" ), this, SLOT(close()) );
|
||
m_topBar->setAutoFillBackground( true );
|
||
showTopBar( false );
|
||
// change topbar background color
|
||
QPalette p = m_topBar->palette();
|
||
p.setColor( QPalette::Active, QPalette::Button, Qt::gray );
|
||
p.setColor( QPalette::Active, QPalette::Background, Qt::darkGray );
|
||
m_topBar->setPalette( p );
|
||
|
||
// Grab swipe gestures to change pages
|
||
grabGesture(Qt::SwipeGesture);
|
||
|
||
// misc stuff
|
||
setMouseTracking( true );
|
||
setContextMenuPolicy( Qt::PreventContextMenu );
|
||
m_transitionTimer = new QTimer( this );
|
||
m_transitionTimer->setSingleShot( true );
|
||
connect(m_transitionTimer, &QTimer::timeout, this, &PresentationWidget::slotTransitionStep);
|
||
m_overlayHideTimer = new QTimer( this );
|
||
m_overlayHideTimer->setSingleShot( true );
|
||
connect(m_overlayHideTimer, &QTimer::timeout, this, &PresentationWidget::slotHideOverlay);
|
||
m_nextPageTimer = new QTimer( this );
|
||
m_nextPageTimer->setSingleShot( true );
|
||
connect(m_nextPageTimer, &QTimer::timeout, this, &PresentationWidget::slotNextPage);
|
||
|
||
connect(m_document, &Okular::Document::processMovieAction, this, &PresentationWidget::slotProcessMovieAction);
|
||
connect(m_document, &Okular::Document::processRenditionAction, this, &PresentationWidget::slotProcessRenditionAction);
|
||
|
||
// handle cursor appearance as specified in configuration
|
||
if ( Okular::Settings::slidesCursor() == Okular::Settings::EnumSlidesCursor::HiddenDelay )
|
||
{
|
||
KCursor::setAutoHideCursor( this, true );
|
||
KCursor::setHideCursorDelay( 3000 );
|
||
}
|
||
else if ( Okular::Settings::slidesCursor() == Okular::Settings::EnumSlidesCursor::Hidden )
|
||
{
|
||
setCursor( QCursor( Qt::BlankCursor ) );
|
||
}
|
||
|
||
setupActions();
|
||
|
||
// inhibit power management
|
||
inhibitPowerManagement();
|
||
|
||
show();
|
||
|
||
QTimer::singleShot( 0, this, &PresentationWidget::slotDelayedEvents );
|
||
|
||
// setFocus() so KCursor::setAutoHideCursor() goes into effect if it's enabled
|
||
setFocus( Qt::OtherFocusReason );
|
||
}
|
||
|
||
PresentationWidget::~PresentationWidget()
|
||
{
|
||
// allow power management saver again
|
||
allowPowerManagement();
|
||
|
||
// stop the audio playbacks
|
||
Okular::AudioPlayer::instance()->stopPlaybacks();
|
||
|
||
// remove our highlights
|
||
if ( m_searchBar )
|
||
{
|
||
m_document->resetSearch( PRESENTATION_SEARCH_ID );
|
||
}
|
||
|
||
// remove this widget from document observer
|
||
m_document->removeObserver( this );
|
||
|
||
foreach( QAction *action, m_topBar->actions() )
|
||
{
|
||
action->setChecked( false );
|
||
action->setEnabled( false );
|
||
}
|
||
|
||
delete m_drawingEngine;
|
||
|
||
// delete frames
|
||
QVector< PresentationFrame * >::iterator fIt = m_frames.begin(), fEnd = m_frames.end();
|
||
for ( ; fIt != fEnd; ++fIt )
|
||
delete *fIt;
|
||
}
|
||
|
||
|
||
void PresentationWidget::notifySetup( const QVector< Okular::Page * > & pageSet, int setupFlags )
|
||
{
|
||
// same document, nothing to change - here we assume the document sets up
|
||
// us with the whole document set as first notifySetup()
|
||
if ( !( setupFlags & Okular::DocumentObserver::DocumentChanged ) )
|
||
return;
|
||
|
||
// delete previous frames (if any (shouldn't be))
|
||
QVector< PresentationFrame * >::iterator fIt = m_frames.begin(), fEnd = m_frames.end();
|
||
for ( ; fIt != fEnd; ++fIt )
|
||
delete *fIt;
|
||
if ( !m_frames.isEmpty() )
|
||
qCWarning(OkularUiDebug) << "Frames setup changed while a Presentation is in progress.";
|
||
m_frames.clear();
|
||
|
||
// create the new frames
|
||
QVector< Okular::Page * >::const_iterator setIt = pageSet.begin(), setEnd = pageSet.end();
|
||
float screenRatio = (float)m_height / (float)m_width;
|
||
for ( ; setIt != setEnd; ++setIt )
|
||
{
|
||
PresentationFrame * frame = new PresentationFrame();
|
||
frame->page = *setIt;
|
||
const QLinkedList< Okular::Annotation * > annotations = (*setIt)->annotations();
|
||
QLinkedList< Okular::Annotation * >::const_iterator aIt = annotations.begin(), aEnd = annotations.end();
|
||
for ( ; aIt != aEnd; ++aIt )
|
||
{
|
||
Okular::Annotation * a = *aIt;
|
||
if ( a->subType() == Okular::Annotation::AMovie )
|
||
{
|
||
Okular::MovieAnnotation * movieAnn = static_cast< Okular::MovieAnnotation * >( a );
|
||
VideoWidget * vw = new VideoWidget( movieAnn, movieAnn->movie(), m_document, this );
|
||
frame->videoWidgets.insert( movieAnn->movie(), vw );
|
||
vw->pageInitialized();
|
||
}
|
||
else if ( a->subType() == Okular::Annotation::ARichMedia )
|
||
{
|
||
Okular::RichMediaAnnotation * richMediaAnn = static_cast< Okular::RichMediaAnnotation * >( a );
|
||
if ( richMediaAnn->movie() ) {
|
||
VideoWidget * vw = new VideoWidget( richMediaAnn, richMediaAnn->movie(), m_document, this );
|
||
frame->videoWidgets.insert( richMediaAnn->movie(), vw );
|
||
vw->pageInitialized();
|
||
}
|
||
}
|
||
else if ( a->subType() == Okular::Annotation::AScreen )
|
||
{
|
||
const Okular::ScreenAnnotation * screenAnn = static_cast< Okular::ScreenAnnotation * >( a );
|
||
Okular::Movie *movie = GuiUtils::renditionMovieFromScreenAnnotation( screenAnn );
|
||
if ( movie )
|
||
{
|
||
VideoWidget * vw = new VideoWidget( screenAnn, movie, m_document, this );
|
||
frame->videoWidgets.insert( movie, vw );
|
||
vw->pageInitialized();
|
||
}
|
||
}
|
||
}
|
||
frame->recalcGeometry( m_width, m_height, screenRatio );
|
||
// add the frame to the vector
|
||
m_frames.push_back( frame );
|
||
}
|
||
|
||
// get metadata from the document
|
||
m_metaStrings.clear();
|
||
const Okular::DocumentInfo info = m_document->documentInfo( QSet<Okular::DocumentInfo::Key>() << Okular::DocumentInfo::Title << Okular::DocumentInfo::Author );
|
||
if ( !info.get( Okular::DocumentInfo::Title ).isNull() )
|
||
m_metaStrings += i18n( "Title: %1", info.get( Okular::DocumentInfo::Title ) );
|
||
if ( !info.get( Okular::DocumentInfo::Author ).isNull() )
|
||
m_metaStrings += i18n( "Author: %1", info.get( Okular::DocumentInfo::Author ) );
|
||
m_metaStrings += i18n( "Pages: %1", m_document->pages() );
|
||
m_metaStrings += i18n( "Click to begin" );
|
||
|
||
m_isSetup = true;
|
||
}
|
||
|
||
void PresentationWidget::notifyViewportChanged( bool /*smoothMove*/ )
|
||
{
|
||
// display the current page
|
||
changePage( m_document->viewport().pageNumber );
|
||
|
||
// auto advance to the next page if set
|
||
startAutoChangeTimer();
|
||
}
|
||
|
||
void PresentationWidget::notifyPageChanged( int pageNumber, int changedFlags )
|
||
{
|
||
// if we are blocking the notifications, do nothing
|
||
if ( m_blockNotifications )
|
||
return;
|
||
|
||
// check if it's the last requested pixmap. if so update the widget.
|
||
if ( (changedFlags & ( DocumentObserver::Pixmap | DocumentObserver::Annotations | DocumentObserver::Highlights ) ) && pageNumber == m_frameIndex )
|
||
generatePage( changedFlags & ( DocumentObserver::Annotations | DocumentObserver::Highlights ) );
|
||
}
|
||
|
||
void PresentationWidget::notifyCurrentPageChanged( int previousPage, int currentPage )
|
||
{
|
||
if ( previousPage != -1 )
|
||
{
|
||
// stop video playback
|
||
Q_FOREACH ( VideoWidget *vw, m_frames[ previousPage ]->videoWidgets )
|
||
{
|
||
vw->stop();
|
||
vw->pageLeft();
|
||
}
|
||
|
||
// stop audio playback, if any
|
||
Okular::AudioPlayer::instance()->stopPlaybacks();
|
||
|
||
// perform the page closing action, if any
|
||
if ( m_document->page( previousPage )->pageAction( Okular::Page::Closing ) )
|
||
m_document->processAction( m_document->page( previousPage )->pageAction( Okular::Page::Closing ) );
|
||
|
||
// perform the additional actions of the page's annotations, if any
|
||
Q_FOREACH ( const Okular::Annotation *annotation, m_document->page( previousPage )->annotations() )
|
||
{
|
||
Okular::Action *action = nullptr;
|
||
|
||
if ( annotation->subType() == Okular::Annotation::AScreen )
|
||
action = static_cast<const Okular::ScreenAnnotation*>( annotation )->additionalAction( Okular::Annotation::PageClosing );
|
||
else if ( annotation->subType() == Okular::Annotation::AWidget )
|
||
action = static_cast<const Okular::WidgetAnnotation*>( annotation )->additionalAction( Okular::Annotation::PageClosing );
|
||
|
||
if ( action )
|
||
m_document->processAction( action );
|
||
}
|
||
}
|
||
|
||
if ( currentPage != -1 )
|
||
{
|
||
m_frameIndex = currentPage;
|
||
|
||
// check if pixmap exists or else request it
|
||
PresentationFrame * frame = m_frames[ m_frameIndex ];
|
||
int pixW = frame->geometry.width();
|
||
int pixH = frame->geometry.height();
|
||
|
||
bool signalsBlocked = m_pagesEdit->signalsBlocked();
|
||
m_pagesEdit->blockSignals( true );
|
||
m_pagesEdit->setText( QString::number( m_frameIndex + 1 ) );
|
||
m_pagesEdit->blockSignals( signalsBlocked );
|
||
|
||
// if pixmap not inside the Okular::Page we request it and wait for
|
||
// notifyPixmapChanged call or else we can proceed to pixmap generation
|
||
if ( !frame->page->hasPixmap( this, ceil(pixW * qApp->devicePixelRatio()), ceil(pixH * qApp->devicePixelRatio()) ) )
|
||
{
|
||
requestPixmaps();
|
||
}
|
||
else
|
||
{
|
||
// make the background pixmap
|
||
generatePage();
|
||
}
|
||
|
||
// perform the page opening action, if any
|
||
if ( m_document->page( m_frameIndex )->pageAction( Okular::Page::Opening ) )
|
||
m_document->processAction( m_document->page( m_frameIndex )->pageAction( Okular::Page::Opening ) );
|
||
|
||
// perform the additional actions of the page's annotations, if any
|
||
Q_FOREACH ( const Okular::Annotation *annotation, m_document->page( m_frameIndex )->annotations() )
|
||
{
|
||
Okular::Action *action = nullptr;
|
||
|
||
if ( annotation->subType() == Okular::Annotation::AScreen )
|
||
action = static_cast<const Okular::ScreenAnnotation*>( annotation )->additionalAction( Okular::Annotation::PageOpening );
|
||
else if ( annotation->subType() == Okular::Annotation::AWidget )
|
||
action = static_cast<const Okular::WidgetAnnotation*>( annotation )->additionalAction( Okular::Annotation::PageOpening );
|
||
|
||
if ( action )
|
||
m_document->processAction( action );
|
||
}
|
||
|
||
// start autoplay video playback
|
||
Q_FOREACH ( VideoWidget *vw, m_frames[ m_frameIndex ]->videoWidgets )
|
||
vw->pageEntered();
|
||
}
|
||
}
|
||
|
||
bool PresentationWidget::canUnloadPixmap( int pageNumber ) const
|
||
{
|
||
if ( Okular::SettingsCore::memoryLevel() == Okular::SettingsCore::EnumMemoryLevel::Low ||
|
||
Okular::SettingsCore::memoryLevel() == Okular::SettingsCore::EnumMemoryLevel::Normal )
|
||
{
|
||
// can unload all pixmaps except for the currently visible one
|
||
return pageNumber != m_frameIndex;
|
||
}
|
||
else
|
||
{
|
||
// can unload all pixmaps except for the currently visible one, previous and next
|
||
return qAbs(pageNumber - m_frameIndex) <= 1;
|
||
}
|
||
}
|
||
|
||
void PresentationWidget::setupActions()
|
||
{
|
||
addAction( m_ac->action( QStringLiteral("first_page") ) );
|
||
addAction( m_ac->action( QStringLiteral("last_page") ) );
|
||
addAction( m_ac->action( QString::fromLocal8Bit(KStandardAction::name( KStandardAction::Prior ) ) ) );
|
||
addAction( m_ac->action( QString::fromLocal8Bit(KStandardAction::name( KStandardAction::Next ) ) ) );
|
||
addAction( m_ac->action( QString::fromLocal8Bit(KStandardAction::name( KStandardAction::DocumentBack ) ) ) );
|
||
addAction( m_ac->action( QString::fromLocal8Bit(KStandardAction::name( KStandardAction::DocumentForward ) ) ) );
|
||
|
||
QAction *action = m_ac->action( QStringLiteral("switch_blackscreen_mode") );
|
||
connect(action, &QAction::toggled, this, &PresentationWidget::toggleBlackScreenMode);
|
||
action->setEnabled( true );
|
||
addAction( action );
|
||
}
|
||
|
||
void PresentationWidget::setPlayPauseIcon()
|
||
{
|
||
QAction *playPauseAction = m_ac->action( QStringLiteral("presentation_play_pause") );
|
||
if ( m_advanceSlides )
|
||
{
|
||
playPauseAction->setIcon( QIcon::fromTheme( QStringLiteral("media-playback-pause") ) );
|
||
playPauseAction->setToolTip( i18nc( "For Presentation", "Pause" ) );
|
||
}
|
||
else
|
||
{
|
||
playPauseAction->setIcon( QIcon::fromTheme( QStringLiteral("media-playback-start") ) );
|
||
playPauseAction->setToolTip( i18nc( "For Presentation", "Play" ) );
|
||
}
|
||
}
|
||
|
||
// <widget events>
|
||
bool PresentationWidget::event( QEvent * e )
|
||
{
|
||
if ( e->type() == QEvent::Gesture )
|
||
return gestureEvent(static_cast<QGestureEvent*>(e));
|
||
|
||
if ( e->type() == QEvent::ToolTip )
|
||
{
|
||
QHelpEvent * he = (QHelpEvent*)e;
|
||
|
||
QRect r;
|
||
const Okular::Action * link = getLink( he->x(), he->y(), &r );
|
||
|
||
if ( link )
|
||
{
|
||
QString tip = link->actionTip();
|
||
if ( !tip.isEmpty() )
|
||
QToolTip::showText( he->globalPos(), tip, this, r );
|
||
}
|
||
e->accept();
|
||
return true;
|
||
}
|
||
else
|
||
// do not stop the event
|
||
return QWidget::event( e );
|
||
}
|
||
|
||
bool PresentationWidget::gestureEvent( QGestureEvent * event )
|
||
{
|
||
// Swiping left or right on a touch screen will go to the previous or next slide, respectively.
|
||
// The precise gesture is the standard Qt swipe: with three(!) fingers.
|
||
if (QGesture *swipe = event->gesture(Qt::SwipeGesture))
|
||
{
|
||
QSwipeGesture * swipeEvent = static_cast<QSwipeGesture *>(swipe);
|
||
|
||
if (swipeEvent->state() == Qt::GestureFinished)
|
||
{
|
||
if (swipeEvent->horizontalDirection() == QSwipeGesture::Left)
|
||
{
|
||
slotPrevPage();
|
||
event->accept();
|
||
return true;
|
||
}
|
||
if (swipeEvent->horizontalDirection() == QSwipeGesture::Right)
|
||
{
|
||
slotNextPage();
|
||
event->accept();
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
void PresentationWidget::keyPressEvent( QKeyEvent * e )
|
||
{
|
||
if ( !m_isSetup )
|
||
return;
|
||
|
||
switch ( e->key() )
|
||
{
|
||
case Qt::Key_Left:
|
||
case Qt::Key_Backspace:
|
||
case Qt::Key_PageUp:
|
||
case Qt::Key_Up:
|
||
slotPrevPage();
|
||
break;
|
||
case Qt::Key_Right:
|
||
case Qt::Key_Space:
|
||
case Qt::Key_PageDown:
|
||
case Qt::Key_Down:
|
||
slotNextPage();
|
||
break;
|
||
case Qt::Key_Home:
|
||
slotFirstPage();
|
||
break;
|
||
case Qt::Key_End:
|
||
slotLastPage();
|
||
break;
|
||
case Qt::Key_Escape:
|
||
if ( !m_topBar->isHidden() )
|
||
showTopBar( false );
|
||
else
|
||
close();
|
||
break;
|
||
}
|
||
}
|
||
|
||
void PresentationWidget::wheelEvent( QWheelEvent * e )
|
||
{
|
||
if ( !m_isSetup )
|
||
return;
|
||
|
||
// performance note: don't remove the clipping
|
||
int div = e->delta() / 120;
|
||
if ( div > 0 )
|
||
{
|
||
if ( div > 3 )
|
||
div = 3;
|
||
while ( div-- )
|
||
slotPrevPage();
|
||
}
|
||
else if ( div < 0 )
|
||
{
|
||
if ( div < -3 )
|
||
div = -3;
|
||
while ( div++ )
|
||
slotNextPage();
|
||
}
|
||
}
|
||
|
||
void PresentationWidget::mousePressEvent( QMouseEvent * e )
|
||
{
|
||
if ( !m_isSetup )
|
||
return;
|
||
|
||
if ( m_drawingEngine )
|
||
{
|
||
QRect r = routeMouseDrawingEvent( e );
|
||
if ( r.isValid() )
|
||
{
|
||
m_drawingRect |= r.translated( m_frames[ m_frameIndex ]->geometry.topLeft() );
|
||
update( m_drawingRect );
|
||
}
|
||
return;
|
||
}
|
||
|
||
// pressing left button
|
||
if ( e->button() == Qt::LeftButton )
|
||
{
|
||
// if pressing on a link, skip other checks
|
||
if ( ( m_pressedLink = getLink( e->x(), e->y() ) ) )
|
||
return;
|
||
|
||
const Okular::Annotation *annotation = getAnnotation( e->x(), e->y() );
|
||
if ( annotation )
|
||
{
|
||
if ( annotation->subType() == Okular::Annotation::AMovie )
|
||
{
|
||
const Okular::MovieAnnotation *movieAnnotation = static_cast<const Okular::MovieAnnotation*>( annotation );
|
||
|
||
VideoWidget *vw = m_frames[ m_frameIndex ]->videoWidgets.value( movieAnnotation->movie() );
|
||
vw->show();
|
||
vw->play();
|
||
return;
|
||
}
|
||
else if ( annotation->subType() == Okular::Annotation::ARichMedia )
|
||
{
|
||
const Okular::RichMediaAnnotation *richMediaAnnotation = static_cast<const Okular::RichMediaAnnotation*>( annotation );
|
||
|
||
VideoWidget *vw = m_frames[ m_frameIndex ]->videoWidgets.value( richMediaAnnotation->movie() );
|
||
vw->show();
|
||
vw->play();
|
||
return;
|
||
}
|
||
else if ( annotation->subType() == Okular::Annotation::AScreen )
|
||
{
|
||
m_document->processAction( static_cast<const Okular::ScreenAnnotation*>( annotation )->action() );
|
||
return;
|
||
}
|
||
}
|
||
|
||
// handle clicking on top-right overlay
|
||
if ( !( Okular::Settings::slidesCursor() == Okular::Settings::EnumSlidesCursor::Hidden ) &&
|
||
m_overlayGeometry.contains( e->pos() ) )
|
||
{
|
||
overlayClick( e->pos() );
|
||
return;
|
||
}
|
||
|
||
m_goToNextPageOnRelease = true;
|
||
}
|
||
// pressing the "move forward" mouse button: unlike the left button this
|
||
// always means "show next page", so we unconditionally delegate to that
|
||
// action on mouse button press
|
||
else if ( e->button() == Qt::ForwardButton ) {
|
||
slotNextPage();
|
||
}
|
||
// pressing right or backward button
|
||
else if ( e->button() == Qt::RightButton || e->button() == Qt::BackButton )
|
||
slotPrevPage();
|
||
}
|
||
|
||
void PresentationWidget::mouseReleaseEvent( QMouseEvent * e )
|
||
{
|
||
if ( m_drawingEngine )
|
||
{
|
||
routeMouseDrawingEvent( e );
|
||
return;
|
||
}
|
||
|
||
// if releasing on the same link we pressed over, execute it
|
||
if ( m_pressedLink && e->button() == Qt::LeftButton )
|
||
{
|
||
const Okular::Action * link = getLink( e->x(), e->y() );
|
||
if ( link == m_pressedLink )
|
||
m_document->processAction( link );
|
||
m_pressedLink = nullptr;
|
||
}
|
||
|
||
if ( m_goToNextPageOnRelease ) {
|
||
slotNextPage();
|
||
m_goToNextPageOnRelease = false;
|
||
}
|
||
}
|
||
|
||
void PresentationWidget::mouseMoveEvent( QMouseEvent * e )
|
||
{
|
||
// safety check
|
||
if ( !m_isSetup )
|
||
return;
|
||
|
||
// update cursor and tooltip if hovering a link
|
||
if ( !m_drawingEngine && Okular::Settings::slidesCursor() != Okular::Settings::EnumSlidesCursor::Hidden )
|
||
testCursorOnLink( e->x(), e->y() );
|
||
|
||
if ( !m_topBar->isHidden() )
|
||
{
|
||
// hide a shown bar when exiting the area
|
||
if ( e->y() > ( m_topBar->height() + 1 ) )
|
||
{
|
||
showTopBar( false );
|
||
setFocus( Qt::OtherFocusReason );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if ( m_drawingEngine && e->buttons() != Qt::NoButton )
|
||
{
|
||
QRect r = routeMouseDrawingEvent( e );
|
||
if ( r.isValid() )
|
||
{
|
||
m_drawingRect |= r.translated( m_frames[ m_frameIndex ]->geometry.topLeft() );
|
||
update( m_drawingRect );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// show the bar if reaching top 2 pixels
|
||
if ( e->y() <= 1 )
|
||
showTopBar( true );
|
||
// handle "dragging the wheel" if clicking on its geometry
|
||
else if ( ( QApplication::mouseButtons() & Qt::LeftButton ) && m_overlayGeometry.contains( e->pos() ) )
|
||
overlayClick( e->pos() );
|
||
}
|
||
}
|
||
}
|
||
|
||
void PresentationWidget::paintEvent( QPaintEvent * pe )
|
||
{
|
||
qreal dpr = devicePixelRatioF();
|
||
|
||
if ( m_inBlackScreenMode )
|
||
{
|
||
QPainter painter( this );
|
||
painter.fillRect( pe->rect(), Qt::black );
|
||
return;
|
||
}
|
||
|
||
if ( !m_isSetup )
|
||
{
|
||
m_width = width();
|
||
m_height = height();
|
||
|
||
connect(m_document, &Okular::Document::linkFind, this, &PresentationWidget::slotFind);
|
||
|
||
// register this observer in document. events will come immediately
|
||
m_document->addObserver( this );
|
||
|
||
// show summary if requested
|
||
if ( Okular::Settings::slidesShowSummary() )
|
||
generatePage();
|
||
}
|
||
|
||
// check painting rect consistancy
|
||
QRect r = pe->rect().intersected( QRect( QPoint( 0, 0 ), geometry().size() ) );
|
||
if ( r.isNull() )
|
||
return;
|
||
|
||
if ( m_lastRenderedPixmap.isNull() )
|
||
{
|
||
QPainter painter( this );
|
||
painter.fillRect( pe->rect(), Okular::Settings::slidesBackgroundColor() );
|
||
return;
|
||
}
|
||
|
||
// blit the pixmap to the screen
|
||
QVector<QRect> allRects = pe->region().rects();
|
||
uint numRects = allRects.count();
|
||
QPainter painter( this );
|
||
for ( uint i = 0; i < numRects; i++ )
|
||
{
|
||
const QRect & r = allRects[i];
|
||
if ( !r.isValid() )
|
||
continue;
|
||
#ifdef ENABLE_PROGRESS_OVERLAY
|
||
const QRect dR(QRectF(r.x() * dpr, r.y() * dpr, r.width() * dpr, r.height() * dpr).toAlignedRect());
|
||
if ( Okular::Settings::slidesShowProgress() && r.intersects( m_overlayGeometry ) )
|
||
{
|
||
// backbuffer the overlay operation
|
||
QPixmap backPixmap( dR.size() );
|
||
backPixmap.setDevicePixelRatio( dpr );
|
||
QPainter pixPainter( &backPixmap );
|
||
|
||
// first draw the background on the backbuffer
|
||
pixPainter.drawPixmap( QPoint(0,0), m_lastRenderedPixmap, dR );
|
||
|
||
// then blend the overlay (a piece of) over the background
|
||
QRect ovr = m_overlayGeometry.intersected( r );
|
||
pixPainter.drawPixmap( (ovr.left() - r.left()), (ovr.top() - r.top()),
|
||
m_lastRenderedOverlay, (ovr.left() - m_overlayGeometry.left()) * dpr,
|
||
(ovr.top() - m_overlayGeometry.top()) * dpr, ovr.width() * dpr, ovr.height() * dpr );
|
||
|
||
// finally blit the pixmap to the screen
|
||
pixPainter.end();
|
||
const QRect backPixmapRect = backPixmap.rect();
|
||
const QRect dBackPixmapRect(QRectF(backPixmapRect.x() * dpr, backPixmapRect.y() * dpr, backPixmapRect.width() * dpr, backPixmapRect.height() * dpr).toAlignedRect());
|
||
painter.drawPixmap( r.topLeft(), backPixmap, dBackPixmapRect );
|
||
} else
|
||
#endif
|
||
// copy the rendered pixmap to the screen
|
||
painter.drawPixmap( r.topLeft(), m_lastRenderedPixmap, dR );
|
||
}
|
||
|
||
// paint drawings
|
||
if ( m_frameIndex != -1 )
|
||
{
|
||
painter.save();
|
||
|
||
const QRect & geom = m_frames[ m_frameIndex ]->geometry;
|
||
|
||
QPixmap pm( geom.size() );
|
||
pm.fill( Qt::transparent );
|
||
QPainter pmPainter( &pm );
|
||
|
||
pmPainter.setRenderHints( QPainter::Antialiasing );
|
||
foreach ( const SmoothPath &drawing, m_frames[ m_frameIndex ]->drawings )
|
||
drawing.paint( &pmPainter, geom.width(), geom.height() );
|
||
|
||
if ( m_drawingEngine && m_drawingRect.intersects( pe->rect() ) )
|
||
m_drawingEngine->paint( &pmPainter, geom.width(), geom.height(), m_drawingRect.intersected( pe->rect() ) );
|
||
|
||
painter.setRenderHints( QPainter::Antialiasing );
|
||
painter.drawPixmap( geom.topLeft() , pm );
|
||
|
||
painter.restore();
|
||
}
|
||
painter.end();
|
||
}
|
||
|
||
void PresentationWidget::resizeEvent( QResizeEvent *re )
|
||
{
|
||
// qCDebug(OkularUiDebug) << re->oldSize() << "=>" << re->size();
|
||
if ( re->oldSize() == QSize( -1, -1 ) )
|
||
return;
|
||
|
||
m_screen = QApplication::desktop()->screenNumber( this );
|
||
|
||
applyNewScreenSize( re->oldSize() );
|
||
}
|
||
|
||
void PresentationWidget::leaveEvent( QEvent * e )
|
||
{
|
||
Q_UNUSED( e )
|
||
|
||
if ( !m_topBar->isHidden() )
|
||
{
|
||
showTopBar( false );
|
||
}
|
||
}
|
||
// </widget events>
|
||
|
||
const void * PresentationWidget::getObjectRect( Okular::ObjectRect::ObjectType type, int x, int y, QRect * geometry ) const
|
||
{
|
||
// no links on invalid pages
|
||
if ( geometry && !geometry->isNull() )
|
||
geometry->setRect( 0, 0, 0, 0 );
|
||
if ( m_frameIndex < 0 || m_frameIndex >= (int)m_frames.size() )
|
||
return nullptr;
|
||
|
||
// get frame, page and geometry
|
||
const PresentationFrame * frame = m_frames[ m_frameIndex ];
|
||
const Okular::Page * page = frame->page;
|
||
const QRect & frameGeometry = frame->geometry;
|
||
|
||
// compute normalized x and y
|
||
double nx = (double)(x - frameGeometry.left()) / (double)frameGeometry.width();
|
||
double ny = (double)(y - frameGeometry.top()) / (double)frameGeometry.height();
|
||
|
||
// no links outside the pages
|
||
if ( nx < 0 || nx > 1 || ny < 0 || ny > 1 )
|
||
return nullptr;
|
||
|
||
// check if 1) there is an object and 2) it's a link
|
||
const QRect d = QApplication::desktop()->screenGeometry( m_screen );
|
||
const Okular::ObjectRect * object = page->objectRect( type, nx, ny, d.width(), d.height() );
|
||
if ( !object )
|
||
return nullptr;
|
||
|
||
// compute link geometry if destination rect present
|
||
if ( geometry )
|
||
{
|
||
*geometry = object->boundingRect( frameGeometry.width(), frameGeometry.height() );
|
||
geometry->translate( frameGeometry.left(), frameGeometry.top() );
|
||
}
|
||
|
||
// return the link pointer
|
||
return object->object();
|
||
}
|
||
|
||
const Okular::Action * PresentationWidget::getLink( int x, int y, QRect * geometry ) const
|
||
{
|
||
return reinterpret_cast<const Okular::Action*>( getObjectRect( Okular::ObjectRect::Action, x, y, geometry ) );
|
||
}
|
||
|
||
const Okular::Annotation * PresentationWidget::getAnnotation( int x, int y, QRect * geometry ) const
|
||
{
|
||
return reinterpret_cast<const Okular::Annotation*>( getObjectRect( Okular::ObjectRect::OAnnotation, x, y, geometry ) );
|
||
}
|
||
|
||
void PresentationWidget::testCursorOnLink( int x, int y )
|
||
{
|
||
const Okular::Action * link = getLink( x, y, nullptr );
|
||
const Okular::Annotation *annotation = getAnnotation( x, y, nullptr );
|
||
|
||
const bool needsHandCursor = ( ( link != nullptr ) ||
|
||
( ( annotation != nullptr ) && ( annotation->subType() == Okular::Annotation::AMovie ) ) ||
|
||
( ( annotation != nullptr ) && ( annotation->subType() == Okular::Annotation::ARichMedia ) ) ||
|
||
( ( annotation != nullptr ) && ( annotation->subType() == Okular::Annotation::AScreen ) && ( GuiUtils::renditionMovieFromScreenAnnotation( static_cast< const Okular::ScreenAnnotation * >( annotation ) ) != nullptr ) ) );
|
||
|
||
// only react on changes (in/out from a link)
|
||
if ( ( needsHandCursor && !m_handCursor ) || ( !needsHandCursor && m_handCursor ) )
|
||
{
|
||
// change cursor shape
|
||
m_handCursor = needsHandCursor;
|
||
setCursor( QCursor( m_handCursor ? Qt::PointingHandCursor : Qt::ArrowCursor ) );
|
||
}
|
||
}
|
||
|
||
void PresentationWidget::overlayClick( const QPoint & position )
|
||
{
|
||
// clicking the progress indicator
|
||
int xPos = position.x() - m_overlayGeometry.x() - m_overlayGeometry.width() / 2,
|
||
yPos = m_overlayGeometry.height() / 2 - position.y();
|
||
if ( !xPos && !yPos )
|
||
return;
|
||
|
||
// compute angle relative to indicator (note coord transformation)
|
||
float angle = 0.5 + 0.5 * atan2( (double)-xPos, (double)-yPos ) / M_PI;
|
||
int pageIndex = (int)( angle * ( m_frames.count() - 1 ) + 0.5 );
|
||
|
||
// go to selected page
|
||
changePage( pageIndex );
|
||
}
|
||
|
||
void PresentationWidget::changePage( int newPage )
|
||
{
|
||
if ( m_showSummaryView ) {
|
||
m_showSummaryView = false;
|
||
m_frameIndex = -1;
|
||
return;
|
||
}
|
||
|
||
if ( m_frameIndex == newPage )
|
||
return;
|
||
|
||
// switch to newPage
|
||
m_document->setViewportPage( newPage, this );
|
||
|
||
if ( (Okular::Settings::slidesShowSummary() && !m_showSummaryView) || m_frameIndex == -1 )
|
||
notifyCurrentPageChanged( -1, newPage );
|
||
}
|
||
|
||
void PresentationWidget::generatePage( bool disableTransition )
|
||
{
|
||
if ( m_lastRenderedPixmap.isNull() )
|
||
{
|
||
qreal dpr = qApp->devicePixelRatio();
|
||
m_lastRenderedPixmap = QPixmap( m_width * dpr, m_height * dpr );
|
||
m_lastRenderedPixmap.setDevicePixelRatio(dpr);
|
||
|
||
m_previousPagePixmap = QPixmap();
|
||
}
|
||
else
|
||
{
|
||
m_previousPagePixmap = m_lastRenderedPixmap;
|
||
}
|
||
|
||
// opens the painter over the pixmap
|
||
QPainter pixmapPainter;
|
||
pixmapPainter.begin( &m_lastRenderedPixmap );
|
||
// generate welcome page
|
||
if ( m_frameIndex == -1 )
|
||
generateIntroPage( pixmapPainter );
|
||
// generate a normal pixmap with extended margin filling
|
||
if ( m_frameIndex >= 0 && m_frameIndex < (int)m_document->pages() )
|
||
generateContentsPage( m_frameIndex, pixmapPainter );
|
||
pixmapPainter.end();
|
||
|
||
// generate the top-right corner overlay
|
||
#ifdef ENABLE_PROGRESS_OVERLAY
|
||
if ( Okular::Settings::slidesShowProgress() && m_frameIndex != -1 )
|
||
generateOverlay();
|
||
#endif
|
||
|
||
// start transition on pages that have one
|
||
if ( !disableTransition && Okular::Settings::slidesTransitionsEnabled() )
|
||
{
|
||
const Okular::PageTransition * transition = m_frameIndex != -1 ?
|
||
m_frames[ m_frameIndex ]->page->transition() : nullptr;
|
||
if ( transition )
|
||
initTransition( transition );
|
||
else {
|
||
Okular::PageTransition trans = defaultTransition();
|
||
initTransition( &trans );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Okular::PageTransition trans = defaultTransition( Okular::Settings::EnumSlidesTransition::Replace );
|
||
initTransition( &trans );
|
||
}
|
||
|
||
// update cursor + tooltip
|
||
if ( !m_drawingEngine && Okular::Settings::slidesCursor() != Okular::Settings::EnumSlidesCursor::Hidden )
|
||
{
|
||
QPoint p = mapFromGlobal( QCursor::pos() );
|
||
testCursorOnLink( p.x(), p.y() );
|
||
}
|
||
}
|
||
|
||
void PresentationWidget::generateIntroPage( QPainter & p )
|
||
{
|
||
qreal dpr = qApp->devicePixelRatio();
|
||
|
||
// use a vertical gray gradient background
|
||
int blend1 = m_height / 10,
|
||
blend2 = 9 * m_height / 10;
|
||
int baseTint = QColor(Qt::gray).red();
|
||
for ( int i = 0; i < m_height; i++ )
|
||
{
|
||
int k = baseTint;
|
||
if ( i < blend1 )
|
||
k -= (int)( baseTint * (i-blend1)*(i-blend1) / (float)(blend1 * blend1) );
|
||
if ( i > blend2 )
|
||
k += (int)( (255-baseTint) * (i-blend2)*(i-blend2) / (float)(blend1 * blend1) );
|
||
p.fillRect( 0, i, m_width, 1, QColor( k, k, k ) );
|
||
}
|
||
|
||
// draw okular logo in the four corners
|
||
QPixmap logo = DesktopIcon( QStringLiteral("okular"), 64 * dpr );
|
||
logo.setDevicePixelRatio( dpr );
|
||
if ( !logo.isNull() )
|
||
{
|
||
p.drawPixmap( 5, 5, logo );
|
||
p.drawPixmap( m_width - 5 - logo.width(), 5, logo );
|
||
p.drawPixmap( m_width - 5 - logo.width(), m_height - 5 - logo.height(), logo );
|
||
p.drawPixmap( 5, m_height - 5 - logo.height(), logo );
|
||
}
|
||
|
||
// draw metadata text (the last line is 'click to begin')
|
||
int strNum = m_metaStrings.count(),
|
||
strHeight = m_height / ( strNum + 4 ),
|
||
fontHeight = 2 * strHeight / 3;
|
||
QFont font( p.font() );
|
||
font.setPixelSize( fontHeight );
|
||
QFontMetrics metrics( font );
|
||
for ( int i = 0; i < strNum; i++ )
|
||
{
|
||
// set a font to fit text width
|
||
float wScale = (float)metrics.boundingRect( m_metaStrings[i] ).width() / (float)m_width;
|
||
QFont f( font );
|
||
if ( wScale > 1.0 )
|
||
f.setPixelSize( (int)( (float)fontHeight / (float)wScale ) );
|
||
p.setFont( f );
|
||
|
||
// text shadow
|
||
p.setPen( Qt::darkGray );
|
||
p.drawText( 2, m_height / 4 + strHeight * i + 2, m_width, strHeight,
|
||
Qt::AlignHCenter | Qt::AlignVCenter, m_metaStrings[i] );
|
||
// text body
|
||
p.setPen( 128 + (127 * i) / strNum );
|
||
p.drawText( 0, m_height / 4 + strHeight * i, m_width, strHeight,
|
||
Qt::AlignHCenter | Qt::AlignVCenter, m_metaStrings[i] );
|
||
}
|
||
}
|
||
|
||
void PresentationWidget::generateContentsPage( int pageNum, QPainter & p )
|
||
{
|
||
PresentationFrame * frame = m_frames[ pageNum ];
|
||
|
||
// translate painter and contents rect
|
||
QRect geom( frame->geometry );
|
||
p.translate( geom.left(), geom.top() );
|
||
geom.translate( -geom.left(), -geom.top() );
|
||
|
||
// draw the page using the shared PagePainter class
|
||
int flags = PagePainter::Accessibility | PagePainter::Highlights | PagePainter::Annotations;
|
||
|
||
PagePainter::paintPageOnPainter( &p, frame->page, this, flags,
|
||
geom.width(), geom.height(), geom );
|
||
|
||
// restore painter
|
||
p.translate( -frame->geometry.left(), -frame->geometry.top() );
|
||
|
||
// fill unpainted areas with background color
|
||
QRegion unpainted( QRect( 0, 0, m_width, m_height ) );
|
||
QVector<QRect> rects = unpainted.subtracted( frame->geometry ).rects();
|
||
for ( int i = 0; i < rects.count(); i++ )
|
||
{
|
||
const QRect & r = rects[i];
|
||
p.fillRect( r, Okular::Settings::slidesBackgroundColor() );
|
||
}
|
||
}
|
||
|
||
// from Arthur - Qt4 - (is defined elsewhere as 'qt_div_255' to not break final compilation)
|
||
inline int qt_div255(int x) { return (x + (x>>8) + 0x80) >> 8; }
|
||
void PresentationWidget::generateOverlay()
|
||
{
|
||
#ifdef ENABLE_PROGRESS_OVERLAY
|
||
qreal dpr = qApp->devicePixelRatio();
|
||
|
||
// calculate overlay geometry and resize pixmap if needed
|
||
int side = m_width / 16;
|
||
m_overlayGeometry.setRect( m_width - side - 4, 4, side, side );
|
||
|
||
// note: to get a sort of antialiasing, we render the pixmap double sized
|
||
// and the resulting image is smoothly scaled down. So here we open a
|
||
// painter on the double sized pixmap.
|
||
side *= 2;
|
||
|
||
QPixmap doublePixmap( side * dpr, side * dpr );
|
||
doublePixmap.setDevicePixelRatio( dpr );
|
||
doublePixmap.fill( Qt::black );
|
||
QPainter pixmapPainter( &doublePixmap );
|
||
pixmapPainter.setRenderHints( QPainter::Antialiasing );
|
||
|
||
// draw PIE SLICES in blue levels (the levels will then be the alpha component)
|
||
int pages = m_document->pages();
|
||
if ( pages > 28 )
|
||
{ // draw continuous slices
|
||
int degrees = (int)( 360 * (float)(m_frameIndex + 1) / (float)pages );
|
||
pixmapPainter.setPen( 0x05 );
|
||
pixmapPainter.setBrush( QColor( 0x40 ) );
|
||
pixmapPainter.drawPie( 2, 2, side - 4, side - 4, 90*16, (360-degrees)*16 );
|
||
pixmapPainter.setPen( 0x40 );
|
||
pixmapPainter.setBrush( QColor( 0xF0 ) );
|
||
pixmapPainter.drawPie( 2, 2, side - 4, side - 4, 90*16, -degrees*16 );
|
||
}
|
||
else
|
||
{ // draw discrete slices
|
||
float oldCoord = -90;
|
||
for ( int i = 0; i < pages; i++ )
|
||
{
|
||
float newCoord = -90 + 360 * (float)(i + 1) / (float)pages;
|
||
pixmapPainter.setPen( i <= m_frameIndex ? 0x40 : 0x05 );
|
||
pixmapPainter.setBrush( QColor( i <= m_frameIndex ? 0xF0 : 0x40 ) );
|
||
pixmapPainter.drawPie( 2, 2, side - 4, side - 4,
|
||
(int)( -16*(oldCoord + 1) ), (int)( -16*(newCoord - (oldCoord + 2)) ) );
|
||
oldCoord = newCoord;
|
||
}
|
||
}
|
||
int circleOut = side / 4;
|
||
pixmapPainter.setPen( Qt::black );
|
||
pixmapPainter.setBrush( Qt::black );
|
||
pixmapPainter.drawEllipse( circleOut, circleOut, side - 2*circleOut, side - 2*circleOut );
|
||
|
||
// draw TEXT using maximum opacity
|
||
QFont f( pixmapPainter.font() );
|
||
f.setPixelSize( side / 4 );
|
||
pixmapPainter.setFont( f );
|
||
pixmapPainter.setPen( 0xFF );
|
||
// use a little offset to prettify output
|
||
pixmapPainter.drawText( 2, 2, side, side, Qt::AlignCenter, QString::number( m_frameIndex + 1 ) );
|
||
|
||
// end drawing pixmap and halve image
|
||
pixmapPainter.end();
|
||
QImage image( doublePixmap.toImage().scaled( (side / 2) * dpr, (side / 2) * dpr, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) );
|
||
image.setDevicePixelRatio( dpr );
|
||
image = image.convertToFormat( QImage::Format_ARGB32 );
|
||
image.setDevicePixelRatio( dpr );
|
||
|
||
// draw circular shadow using the same technique
|
||
doublePixmap.fill( Qt::black );
|
||
pixmapPainter.begin( &doublePixmap );
|
||
pixmapPainter.setPen( 0x40 );
|
||
pixmapPainter.setBrush( QColor( 0x80 ) );
|
||
pixmapPainter.drawEllipse( 0, 0, side, side );
|
||
pixmapPainter.end();
|
||
QImage shadow( doublePixmap.toImage().scaled( (side / 2) * dpr, (side / 2) * dpr, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) );
|
||
shadow.setDevicePixelRatio( dpr );
|
||
|
||
// generate a 2 colors pixmap using mixing shadow (made with highlight color)
|
||
// and image (made with highlightedText color)
|
||
QPalette pal = palette();
|
||
QColor color = pal.color( QPalette::Active, QPalette::HighlightedText );
|
||
int red = color.red(), green = color.green(), blue = color.blue();
|
||
color = pal.color( QPalette::Active, QPalette::Highlight );
|
||
int sRed = color.red(), sGreen = color.green(), sBlue = color.blue();
|
||
// pointers
|
||
unsigned int * data = (unsigned int *)image.bits(),
|
||
* shadowData = (unsigned int *)shadow.bits(),
|
||
pixels = image.width() * image.height();
|
||
// cache data (reduce computation time to 26%!)
|
||
int c1 = -1, c2 = -1, cR = 0, cG = 0, cB = 0, cA = 0;
|
||
// foreach pixel
|
||
for( unsigned int i = 0; i < pixels; ++i )
|
||
{
|
||
// alpha for shadow and image
|
||
int shadowAlpha = shadowData[i] & 0xFF,
|
||
srcAlpha = data[i] & 0xFF;
|
||
// cache values
|
||
if ( srcAlpha != c1 || shadowAlpha != c2 )
|
||
{
|
||
c1 = srcAlpha;
|
||
c2 = shadowAlpha;
|
||
// fuse color components and alpha value of image over shadow
|
||
data[i] = qRgba(
|
||
cR = qt_div255( srcAlpha * red + (255 - srcAlpha) * sRed ),
|
||
cG = qt_div255( srcAlpha * green + (255 - srcAlpha) * sGreen ),
|
||
cB = qt_div255( srcAlpha * blue + (255 - srcAlpha) * sBlue ),
|
||
cA = qt_div255( srcAlpha * srcAlpha + (255 - srcAlpha) * shadowAlpha )
|
||
);
|
||
}
|
||
else
|
||
data[i] = qRgba( cR, cG, cB, cA );
|
||
}
|
||
m_lastRenderedOverlay = QPixmap::fromImage( image );
|
||
m_lastRenderedOverlay.setDevicePixelRatio( dpr );
|
||
|
||
// start the autohide timer
|
||
//repaint( m_overlayGeometry ); // toggle with next line
|
||
update( m_overlayGeometry );
|
||
m_overlayHideTimer->start( 2500 );
|
||
#endif
|
||
}
|
||
|
||
|
||
QRect PresentationWidget::routeMouseDrawingEvent( QMouseEvent * e )
|
||
{
|
||
if ( m_frameIndex == -1 ) // Can't draw on the summary page
|
||
return QRect();
|
||
|
||
const QRect & geom = m_frames[ m_frameIndex ]->geometry;
|
||
const Okular::Page * page = m_frames[ m_frameIndex ]->page;
|
||
|
||
AnnotatorEngine::EventType eventType;
|
||
AnnotatorEngine::Button button;
|
||
|
||
// figure out the event type and button
|
||
AnnotatorEngine::decodeEvent( e, &eventType, &button );
|
||
|
||
static bool hasclicked = false;
|
||
if ( eventType == AnnotatorEngine::Press )
|
||
hasclicked = true;
|
||
|
||
double nX = ( (double)e->x() - (double)geom.left() ) / (double)geom.width();
|
||
double nY = ( (double)e->y() - (double)geom.top() ) / (double)geom.height();
|
||
QRect ret;
|
||
bool isInside = nX >= 0 && nX < 1 && nY >= 0 && nY < 1;
|
||
|
||
if ( hasclicked && !isInside ) {
|
||
// Fake a move to the last border pos
|
||
nX = qBound(0., nX, 1.);
|
||
nY = qBound(0., nY, 1.);
|
||
m_drawingEngine->event( AnnotatorEngine::Move, button, nX, nY, geom.width(), geom.height(), page );
|
||
|
||
// Fake a release in the following lines
|
||
eventType = AnnotatorEngine::Release;
|
||
isInside = true;
|
||
} else if ( !hasclicked && isInside )
|
||
{
|
||
// we're coming from the outside, pretend we started clicking at the closest border
|
||
if ( nX < ( 1 - nX ) && nX < nY && nX < ( 1 - nY ) )
|
||
nX = 0;
|
||
else if ( nY < ( 1 - nY ) && nY < nX && nY < ( 1 - nX ) )
|
||
nY = 0;
|
||
else if ( ( 1 - nX ) < nX && ( 1 - nX ) < nY && ( 1 - nX ) < ( 1 - nY ) )
|
||
nX = 1;
|
||
else
|
||
nY = 1;
|
||
|
||
hasclicked = true;
|
||
eventType = AnnotatorEngine::Press;
|
||
}
|
||
|
||
if ( hasclicked && isInside )
|
||
{
|
||
ret = m_drawingEngine->event( eventType, button, nX, nY, geom.width(), geom.height(), page );
|
||
}
|
||
|
||
if ( eventType == AnnotatorEngine::Release )
|
||
{
|
||
hasclicked = false;
|
||
}
|
||
|
||
if ( m_drawingEngine->creationCompleted() )
|
||
{
|
||
// add drawing to current page
|
||
m_frames[ m_frameIndex ]->drawings << m_drawingEngine->endSmoothPath();
|
||
|
||
// manually disable and re-enable the pencil mode, so we can do
|
||
// cleaning of the actual drawer and create a new one just after
|
||
// that - that gives continuous drawing
|
||
slotChangeDrawingToolEngine( QDomElement() );
|
||
slotChangeDrawingToolEngine( m_currentDrawingToolElement );
|
||
|
||
// schedule repaint
|
||
update();
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
void PresentationWidget::startAutoChangeTimer()
|
||
{
|
||
double pageDuration = m_frameIndex >= 0 && m_frameIndex < (int)m_frames.count() ? m_frames[ m_frameIndex ]->page->duration() : -1;
|
||
if ( m_advanceSlides || pageDuration >= 0.0 )
|
||
{
|
||
double secs;
|
||
if ( pageDuration < 0.0 )
|
||
secs = Okular::SettingsCore::slidesAdvanceTime();
|
||
else if ( m_advanceSlides )
|
||
secs = qMin<double>( pageDuration, Okular::SettingsCore::slidesAdvanceTime() );
|
||
else
|
||
secs = pageDuration;
|
||
|
||
m_nextPageTimer->start( (int)( secs * 1000 ) );
|
||
}
|
||
}
|
||
|
||
void PresentationWidget::recalcGeometry()
|
||
{
|
||
QDesktopWidget *desktop = QApplication::desktop();
|
||
const int preferenceScreen = Okular::Settings::slidesScreen();
|
||
int screen = 0;
|
||
if ( preferenceScreen == -2 )
|
||
{
|
||
screen = desktop->screenNumber( m_parentWidget );
|
||
}
|
||
else if ( preferenceScreen == -1 )
|
||
{
|
||
screen = desktop->primaryScreen();
|
||
}
|
||
else if ( preferenceScreen >= 0 && preferenceScreen < desktop->numScreens() )
|
||
{
|
||
screen = preferenceScreen;
|
||
}
|
||
else
|
||
{
|
||
screen = desktop->screenNumber( m_parentWidget );
|
||
Okular::Settings::setSlidesScreen( -2 );
|
||
}
|
||
const QRect screenGeom = desktop->screenGeometry( screen );
|
||
// qCDebug(OkularUiDebug) << screen << "=>" << screenGeom;
|
||
m_screen = screen;
|
||
setGeometry( screenGeom );
|
||
}
|
||
|
||
void PresentationWidget::repositionContent()
|
||
{
|
||
const QRect ourGeom = geometry();
|
||
|
||
// tool bar height in pixels, make it large enough to hold the text fields with the page numbers
|
||
const int toolBarHeight = m_pagesEdit->height() * 1.5;
|
||
|
||
m_topBar->setGeometry( 0, 0, ourGeom.width(), toolBarHeight );
|
||
m_topBar->setIconSize( QSize( toolBarHeight * 0.75, toolBarHeight * 0.75 ) );
|
||
|
||
}
|
||
|
||
void PresentationWidget::requestPixmaps()
|
||
{
|
||
PresentationFrame * frame = m_frames[ m_frameIndex ];
|
||
int pixW = frame->geometry.width();
|
||
int pixH = frame->geometry.height();
|
||
|
||
// operation will take long: set busy cursor
|
||
QApplication::setOverrideCursor( QCursor( Qt::BusyCursor ) );
|
||
// request the pixmap
|
||
QLinkedList< Okular::PixmapRequest * > requests;
|
||
requests.push_back( new Okular::PixmapRequest( this, m_frameIndex, pixW, pixH, PRESENTATION_PRIO, Okular::PixmapRequest::NoFeature ) );
|
||
// restore cursor
|
||
QApplication::restoreOverrideCursor();
|
||
// ask for next and previous page if not in low memory usage setting
|
||
if ( Okular::SettingsCore::memoryLevel() != Okular::SettingsCore::EnumMemoryLevel::Low )
|
||
{
|
||
int pagesToPreload = 1;
|
||
|
||
// If greedy, preload everything
|
||
if (Okular::SettingsCore::memoryLevel() == Okular::SettingsCore::EnumMemoryLevel::Greedy)
|
||
pagesToPreload = (int)m_document->pages();
|
||
|
||
Okular::PixmapRequest::PixmapRequestFeatures requestFeatures = Okular::PixmapRequest::Preload;
|
||
requestFeatures |= Okular::PixmapRequest::Asynchronous;
|
||
|
||
for( int j = 1; j <= pagesToPreload; j++ )
|
||
{
|
||
int tailRequest = m_frameIndex + j;
|
||
if ( tailRequest < (int)m_document->pages() )
|
||
{
|
||
PresentationFrame *nextFrame = m_frames[ tailRequest ];
|
||
pixW = nextFrame->geometry.width();
|
||
pixH = nextFrame->geometry.height();
|
||
if ( !nextFrame->page->hasPixmap( this, pixW, pixH ) )
|
||
requests.push_back( new Okular::PixmapRequest( this, tailRequest, pixW, pixH, PRESENTATION_PRELOAD_PRIO, requestFeatures ) );
|
||
}
|
||
|
||
int headRequest = m_frameIndex - j;
|
||
if ( headRequest >= 0 )
|
||
{
|
||
PresentationFrame *prevFrame = m_frames[ headRequest ];
|
||
pixW = prevFrame->geometry.width();
|
||
pixH = prevFrame->geometry.height();
|
||
if ( !prevFrame->page->hasPixmap( this, pixW, pixH ) )
|
||
requests.push_back( new Okular::PixmapRequest( this, headRequest, pixW, pixH, PRESENTATION_PRELOAD_PRIO, requestFeatures ) );
|
||
}
|
||
|
||
// stop if we've already reached both ends of the document
|
||
if ( headRequest < 0 && tailRequest >= (int)m_document->pages() )
|
||
break;
|
||
}
|
||
}
|
||
m_document->requestPixmaps( requests );
|
||
}
|
||
|
||
|
||
void PresentationWidget::slotNextPage()
|
||
{
|
||
int nextIndex = m_frameIndex + 1;
|
||
|
||
// loop when configured
|
||
if ( nextIndex == m_frames.count() && Okular::Settings::slidesLoop() )
|
||
nextIndex = 0;
|
||
|
||
if ( nextIndex < m_frames.count() )
|
||
{
|
||
// go to next page
|
||
changePage( nextIndex );
|
||
// auto advance to the next page if set
|
||
startAutoChangeTimer();
|
||
}
|
||
else
|
||
{
|
||
#ifdef ENABLE_PROGRESS_OVERLAY
|
||
if ( Okular::Settings::slidesShowProgress() )
|
||
generateOverlay();
|
||
#endif
|
||
if ( m_transitionTimer->isActive() )
|
||
{
|
||
m_transitionTimer->stop();
|
||
m_lastRenderedPixmap = m_currentPagePixmap;
|
||
update();
|
||
}
|
||
}
|
||
// we need the setFocus() call here to let KCursor::autoHide() work correctly
|
||
setFocus();
|
||
}
|
||
|
||
void PresentationWidget::slotPrevPage()
|
||
{
|
||
if ( m_frameIndex > 0 )
|
||
{
|
||
// go to previous page
|
||
changePage( m_frameIndex - 1 );
|
||
|
||
// auto advance to the next page if set
|
||
startAutoChangeTimer();
|
||
}
|
||
else
|
||
{
|
||
#ifdef ENABLE_PROGRESS_OVERLAY
|
||
if ( Okular::Settings::slidesShowProgress() )
|
||
generateOverlay();
|
||
#endif
|
||
if ( m_transitionTimer->isActive() )
|
||
{
|
||
m_transitionTimer->stop();
|
||
m_lastRenderedPixmap = m_currentPagePixmap;
|
||
update();
|
||
}
|
||
}
|
||
}
|
||
|
||
void PresentationWidget::slotFirstPage()
|
||
{
|
||
changePage( 0 );
|
||
}
|
||
|
||
void PresentationWidget::slotLastPage()
|
||
{
|
||
changePage( (int)m_frames.count() - 1 );
|
||
}
|
||
|
||
void PresentationWidget::slotHideOverlay()
|
||
{
|
||
QRect geom( m_overlayGeometry );
|
||
m_overlayGeometry.setCoords( 0, 0, -1, -1 );
|
||
update( geom );
|
||
}
|
||
|
||
void PresentationWidget::slotTransitionStep()
|
||
{
|
||
switch( m_currentTransition.type() )
|
||
{
|
||
case Okular::PageTransition::Fade:
|
||
{
|
||
QPainter pixmapPainter;
|
||
m_currentPixmapOpacity += 1.0 / m_transitionSteps;
|
||
m_lastRenderedPixmap = QPixmap( m_lastRenderedPixmap.size() );
|
||
m_lastRenderedPixmap.setDevicePixelRatio( qApp->devicePixelRatio() );
|
||
m_lastRenderedPixmap.fill( Qt::transparent );
|
||
pixmapPainter.begin( &m_lastRenderedPixmap );
|
||
pixmapPainter.setCompositionMode( QPainter::CompositionMode_Source );
|
||
pixmapPainter.setOpacity( 1 - m_currentPixmapOpacity );
|
||
pixmapPainter.drawPixmap( 0, 0, m_previousPagePixmap );
|
||
pixmapPainter.setOpacity( m_currentPixmapOpacity );
|
||
pixmapPainter.drawPixmap( 0, 0, m_currentPagePixmap );
|
||
update();
|
||
if( m_currentPixmapOpacity >= 1 )
|
||
return;
|
||
} break;
|
||
default:
|
||
{
|
||
if ( m_transitionRects.empty() )
|
||
{
|
||
// it's better to fix the transition to cover the whole screen than
|
||
// enabling the following line that wastes cpu for nothing
|
||
//update();
|
||
return;
|
||
}
|
||
|
||
for ( int i = 0; i < m_transitionMul && !m_transitionRects.empty(); i++ )
|
||
{
|
||
update( m_transitionRects.first() );
|
||
m_transitionRects.pop_front();
|
||
}
|
||
} break;
|
||
}
|
||
m_transitionTimer->start( m_transitionDelay );
|
||
}
|
||
|
||
void PresentationWidget::slotDelayedEvents()
|
||
{
|
||
recalcGeometry();
|
||
repositionContent();
|
||
|
||
if ( m_screenSelect )
|
||
{
|
||
m_screenSelect->setCurrentItem( m_screen );
|
||
connect( m_screenSelect->selectableActionGroup(), &QActionGroup::triggered,
|
||
this, &PresentationWidget::chooseScreen );
|
||
}
|
||
|
||
// show widget and take control
|
||
show();
|
||
setWindowState( windowState() | Qt::WindowFullScreen );
|
||
|
||
connect( QApplication::desktop(), &QDesktopWidget::resized, this, &PresentationWidget::screenResized );
|
||
|
||
// inform user on how to exit from presentation mode
|
||
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(), QStringLiteral("presentationInfo") );
|
||
}
|
||
|
||
void PresentationWidget::slotPageChanged()
|
||
{
|
||
bool ok = true;
|
||
int p = m_pagesEdit->text().toInt( &ok );
|
||
if ( !ok )
|
||
return;
|
||
|
||
changePage( p - 1 );
|
||
}
|
||
|
||
void PresentationWidget::slotChangeDrawingToolEngine( const QDomElement &element )
|
||
{
|
||
if ( element.isNull() )
|
||
{
|
||
delete m_drawingEngine;
|
||
m_drawingEngine = nullptr;
|
||
m_drawingRect = QRect();
|
||
setCursor( Qt::ArrowCursor );
|
||
}
|
||
else
|
||
{
|
||
m_drawingEngine = new SmoothPathEngine( element );
|
||
setCursor( QCursor( QPixmap( QStringLiteral("pencil") ), Qt::ArrowCursor ) );
|
||
m_currentDrawingToolElement = element;
|
||
}
|
||
}
|
||
|
||
void PresentationWidget::slotAddDrawingToolActions()
|
||
{
|
||
DrawingToolActions *drawingToolActions = qobject_cast<DrawingToolActions*>(sender());
|
||
foreach(QAction *action, drawingToolActions->actions()) {
|
||
action->setEnabled( true );
|
||
m_topBar->addAction( action );
|
||
addAction( action );
|
||
}
|
||
}
|
||
|
||
void PresentationWidget::clearDrawings()
|
||
{
|
||
if ( m_frameIndex != -1 )
|
||
m_frames[ m_frameIndex ]->drawings.clear();
|
||
update();
|
||
}
|
||
|
||
void PresentationWidget::screenResized( int screen )
|
||
{
|
||
// we can ignore if a screen was resized in the case the screen is not
|
||
// where we are on
|
||
if ( screen != m_screen )
|
||
return;
|
||
|
||
setScreen( screen );
|
||
}
|
||
|
||
void PresentationWidget::chooseScreen( QAction *act )
|
||
{
|
||
if ( !act || act->data().type() != QVariant::Int )
|
||
return;
|
||
|
||
const int newScreen = act->data().toInt();
|
||
|
||
setScreen( newScreen );
|
||
}
|
||
|
||
void PresentationWidget::toggleBlackScreenMode( bool )
|
||
{
|
||
m_inBlackScreenMode = !m_inBlackScreenMode;
|
||
|
||
update();
|
||
}
|
||
|
||
void PresentationWidget::setScreen( int newScreen )
|
||
{
|
||
const QRect screenGeom = QApplication::desktop()->screenGeometry( newScreen );
|
||
const QSize oldSize = size();
|
||
// qCDebug(OkularUiDebug) << newScreen << "=>" << screenGeom;
|
||
m_screen = newScreen;
|
||
setGeometry( screenGeom );
|
||
|
||
applyNewScreenSize( oldSize );
|
||
}
|
||
|
||
void PresentationWidget::applyNewScreenSize( const QSize & oldSize )
|
||
{
|
||
repositionContent();
|
||
|
||
// if by chance the new screen has the same resolution of the previous,
|
||
// do not invalidate pixmaps and such..
|
||
if ( size() == oldSize )
|
||
return;
|
||
|
||
m_width = width();
|
||
m_height = height();
|
||
|
||
// update the frames
|
||
QVector< PresentationFrame * >::const_iterator fIt = m_frames.constBegin(), fEnd = m_frames.constEnd();
|
||
const float screenRatio = (float)m_height / (float)m_width;
|
||
for ( ; fIt != fEnd; ++fIt )
|
||
{
|
||
(*fIt)->recalcGeometry( m_width, m_height, screenRatio );
|
||
}
|
||
|
||
if ( m_frameIndex != -1 )
|
||
{
|
||
// ugliness alarm!
|
||
const_cast< Okular::Page * >( m_frames[ m_frameIndex ]->page )->deletePixmap( this );
|
||
// force the regeneration of the pixmap
|
||
m_lastRenderedPixmap = QPixmap();
|
||
m_blockNotifications = true;
|
||
requestPixmaps();
|
||
m_blockNotifications = false;
|
||
}
|
||
if ( m_transitionTimer->isActive() )
|
||
{
|
||
m_transitionTimer->stop();
|
||
}
|
||
generatePage( true /* no transitions */ );
|
||
}
|
||
|
||
void PresentationWidget::inhibitPowerManagement()
|
||
{
|
||
#ifdef Q_OS_LINUX
|
||
QString reason = i18nc( "Reason for inhibiting the screensaver activation, when the presentation mode is active", "Giving a presentation" );
|
||
|
||
if (!m_screenInhibitCookie) {
|
||
QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.ScreenSaver", "/ScreenSaver",
|
||
"org.freedesktop.ScreenSaver", "Inhibit");
|
||
message << QCoreApplication::applicationName();
|
||
message << reason;
|
||
|
||
QDBusPendingReply<uint> reply = QDBusConnection::sessionBus().asyncCall(message);
|
||
reply.waitForFinished();
|
||
if (reply.isValid()) {
|
||
m_screenInhibitCookie = reply.value();
|
||
qCDebug(OkularUiDebug) << "Screen inhibition cookie" << m_screenInhibitCookie;
|
||
} else {
|
||
qCWarning(OkularUiDebug) << "Unable to inhibit screensaver" << reply.error();
|
||
}
|
||
}
|
||
|
||
if (m_sleepInhibitFd != -1) {
|
||
QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.login1"),
|
||
QStringLiteral("/org/freedesktop/login1"),
|
||
QStringLiteral("org.freedesktop.login1.Manager"),
|
||
QStringLiteral("Inhibit")
|
||
);
|
||
message << QStringLiteral("sleep");
|
||
message << QCoreApplication::applicationName();
|
||
message << reason;
|
||
message << QStringLiteral("block");
|
||
|
||
QDBusPendingReply<QDBusUnixFileDescriptor> reply = QDBusConnection::systemBus().asyncCall(message);
|
||
reply.waitForFinished();
|
||
if (reply.isValid()) {
|
||
m_sleepInhibitFd = dup(reply.value().fileDescriptor());
|
||
} else {
|
||
qCWarning(OkularUiDebug) << "Unable to inhibit sleep" << reply.error();
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
void PresentationWidget::allowPowerManagement()
|
||
{
|
||
#ifdef Q_OS_LINUX
|
||
if (m_sleepInhibitFd != -1) {
|
||
::close(m_sleepInhibitFd);
|
||
m_sleepInhibitFd = -1;
|
||
}
|
||
|
||
if (m_screenInhibitCookie) {
|
||
QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.ScreenSaver", "/ScreenSaver",
|
||
"org.freedesktop.ScreenSaver", "UnInhibit");
|
||
message << m_screenInhibitCookie;
|
||
|
||
QDBusPendingReply<uint> reply = QDBusConnection::sessionBus().asyncCall(message);
|
||
reply.waitForFinished();
|
||
|
||
m_screenInhibitCookie = 0;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
void PresentationWidget::showTopBar( bool show )
|
||
{
|
||
if ( show )
|
||
{
|
||
m_topBar->show();
|
||
|
||
// Don't autohide the mouse cursor if it's over the toolbar
|
||
if ( Okular::Settings::slidesCursor() == Okular::Settings::EnumSlidesCursor::HiddenDelay )
|
||
{
|
||
KCursor::setAutoHideCursor( this, false );
|
||
}
|
||
|
||
// Always show a cursor when topBar is visible
|
||
if ( !m_drawingEngine )
|
||
{
|
||
setCursor( QCursor( Qt::ArrowCursor ) );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
m_topBar->hide();
|
||
|
||
// Reenable autohide if need be when leaving the toolbar
|
||
if ( Okular::Settings::slidesCursor() == Okular::Settings::EnumSlidesCursor::HiddenDelay )
|
||
{
|
||
KCursor::setAutoHideCursor( this, true );
|
||
}
|
||
|
||
// Or hide the cursor again if hidden cursor is enabled
|
||
else if ( Okular::Settings::slidesCursor() == Okular::Settings::EnumSlidesCursor::Hidden )
|
||
{
|
||
// Don't hide the cursor if drawing mode is on
|
||
if ( !m_drawingEngine )
|
||
{
|
||
setCursor( QCursor( Qt::BlankCursor ) );
|
||
}
|
||
}
|
||
}
|
||
|
||
// Make sure mouse tracking isn't off after the KCursor::setAutoHideCursor() calls
|
||
setMouseTracking( true );
|
||
}
|
||
|
||
|
||
void PresentationWidget::slotFind()
|
||
{
|
||
if ( !m_searchBar )
|
||
{
|
||
m_searchBar = new PresentationSearchBar( m_document, this, this );
|
||
m_searchBar->forceSnap();
|
||
}
|
||
m_searchBar->focusOnSearchEdit();
|
||
m_searchBar->show();
|
||
}
|
||
|
||
|
||
const Okular::PageTransition PresentationWidget::defaultTransition() const
|
||
{
|
||
return defaultTransition( Okular::Settings::slidesTransition() );
|
||
}
|
||
|
||
const Okular::PageTransition PresentationWidget::defaultTransition( int type ) const
|
||
{
|
||
switch ( type )
|
||
{
|
||
case Okular::Settings::EnumSlidesTransition::BlindsHorizontal:
|
||
{
|
||
Okular::PageTransition transition( Okular::PageTransition::Blinds );
|
||
transition.setAlignment( Okular::PageTransition::Horizontal );
|
||
return transition;
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::BlindsVertical:
|
||
{
|
||
Okular::PageTransition transition( Okular::PageTransition::Blinds );
|
||
transition.setAlignment( Okular::PageTransition::Vertical );
|
||
return transition;
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::BoxIn:
|
||
{
|
||
Okular::PageTransition transition( Okular::PageTransition::Box );
|
||
transition.setDirection( Okular::PageTransition::Inward );
|
||
return transition;
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::BoxOut:
|
||
{
|
||
Okular::PageTransition transition( Okular::PageTransition::Box );
|
||
transition.setDirection( Okular::PageTransition::Outward );
|
||
return transition;
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::Dissolve:
|
||
{
|
||
return Okular::PageTransition( Okular::PageTransition::Dissolve );
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::GlitterDown:
|
||
{
|
||
Okular::PageTransition transition( Okular::PageTransition::Glitter );
|
||
transition.setAngle( 270 );
|
||
return transition;
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::GlitterRight:
|
||
{
|
||
Okular::PageTransition transition( Okular::PageTransition::Glitter );
|
||
transition.setAngle( 0 );
|
||
return transition;
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::GlitterRightDown:
|
||
{
|
||
Okular::PageTransition transition( Okular::PageTransition::Glitter );
|
||
transition.setAngle( 315 );
|
||
return transition;
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::Random:
|
||
{
|
||
return defaultTransition( KRandom::random() % 18 );
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::SplitHorizontalIn:
|
||
{
|
||
Okular::PageTransition transition( Okular::PageTransition::Split );
|
||
transition.setAlignment( Okular::PageTransition::Horizontal );
|
||
transition.setDirection( Okular::PageTransition::Inward );
|
||
return transition;
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::SplitHorizontalOut:
|
||
{
|
||
Okular::PageTransition transition( Okular::PageTransition::Split );
|
||
transition.setAlignment( Okular::PageTransition::Horizontal );
|
||
transition.setDirection( Okular::PageTransition::Outward );
|
||
return transition;
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::SplitVerticalIn:
|
||
{
|
||
Okular::PageTransition transition( Okular::PageTransition::Split );
|
||
transition.setAlignment( Okular::PageTransition::Vertical );
|
||
transition.setDirection( Okular::PageTransition::Inward );
|
||
return transition;
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::SplitVerticalOut:
|
||
{
|
||
Okular::PageTransition transition( Okular::PageTransition::Split );
|
||
transition.setAlignment( Okular::PageTransition::Vertical );
|
||
transition.setDirection( Okular::PageTransition::Outward );
|
||
return transition;
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::WipeDown:
|
||
{
|
||
Okular::PageTransition transition( Okular::PageTransition::Wipe );
|
||
transition.setAngle( 270 );
|
||
return transition;
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::WipeRight:
|
||
{
|
||
Okular::PageTransition transition( Okular::PageTransition::Wipe );
|
||
transition.setAngle( 0 );
|
||
return transition;
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::WipeLeft:
|
||
{
|
||
Okular::PageTransition transition( Okular::PageTransition::Wipe );
|
||
transition.setAngle( 180 );
|
||
return transition;
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::WipeUp:
|
||
{
|
||
Okular::PageTransition transition( Okular::PageTransition::Wipe );
|
||
transition.setAngle( 90 );
|
||
return transition;
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::Fade:
|
||
{
|
||
return Okular::PageTransition( Okular::PageTransition::Fade );
|
||
break;
|
||
}
|
||
case Okular::Settings::EnumSlidesTransition::Replace:
|
||
default:
|
||
return Okular::PageTransition( Okular::PageTransition::Replace );
|
||
break;
|
||
}
|
||
// should not happen, just make gcc happy
|
||
return Okular::PageTransition();
|
||
}
|
||
|
||
/** ONLY the TRANSITIONS GENERATION function from here on **/
|
||
void PresentationWidget::initTransition( const Okular::PageTransition *transition )
|
||
{
|
||
// if it's just a 'replace' transition, repaint the screen
|
||
if ( transition->type() == Okular::PageTransition::Replace )
|
||
{
|
||
update();
|
||
return;
|
||
}
|
||
|
||
const bool isInward = transition->direction() == Okular::PageTransition::Inward;
|
||
const bool isHorizontal = transition->alignment() == Okular::PageTransition::Horizontal;
|
||
const float totalTime = transition->duration();
|
||
|
||
m_transitionRects.clear();
|
||
m_currentTransition = *transition;
|
||
m_currentPagePixmap = m_lastRenderedPixmap;
|
||
|
||
switch( transition->type() )
|
||
{
|
||
// split: horizontal / vertical and inward / outward
|
||
case Okular::PageTransition::Split:
|
||
{
|
||
const int steps = isHorizontal ? 100 : 75;
|
||
if ( isHorizontal )
|
||
{
|
||
if ( isInward )
|
||
{
|
||
int xPosition = 0;
|
||
for ( int i = 0; i < steps; i++ )
|
||
{
|
||
int xNext = ((i + 1) * m_width) / (2 * steps);
|
||
m_transitionRects.push_back( QRect( xPosition, 0, xNext - xPosition, m_height ) );
|
||
m_transitionRects.push_back( QRect( m_width - xNext, 0, xNext - xPosition, m_height ) );
|
||
xPosition = xNext;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int xPosition = m_width / 2;
|
||
for ( int i = 0; i < steps; i++ )
|
||
{
|
||
int xNext = ((steps - (i + 1)) * m_width) / (2 * steps);
|
||
m_transitionRects.push_back( QRect( xNext, 0, xPosition - xNext, m_height ) );
|
||
m_transitionRects.push_back( QRect( m_width - xPosition, 0, xPosition - xNext, m_height ) );
|
||
xPosition = xNext;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if ( isInward )
|
||
{
|
||
int yPosition = 0;
|
||
for ( int i = 0; i < steps; i++ )
|
||
{
|
||
int yNext = ((i + 1) * m_height) / (2 * steps);
|
||
m_transitionRects.push_back( QRect( 0, yPosition, m_width, yNext - yPosition ) );
|
||
m_transitionRects.push_back( QRect( 0, m_height - yNext, m_width, yNext - yPosition ) );
|
||
yPosition = yNext;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int yPosition = m_height / 2;
|
||
for ( int i = 0; i < steps; i++ )
|
||
{
|
||
int yNext = ((steps - (i + 1)) * m_height) / (2 * steps);
|
||
m_transitionRects.push_back( QRect( 0, yNext, m_width, yPosition - yNext ) );
|
||
m_transitionRects.push_back( QRect( 0, m_height - yPosition, m_width, yPosition - yNext ) );
|
||
yPosition = yNext;
|
||
}
|
||
}
|
||
}
|
||
m_transitionMul = 2;
|
||
m_transitionDelay = (int)( (totalTime * 1000) / steps );
|
||
} break;
|
||
|
||
// blinds: horizontal(l-to-r) / vertical(t-to-b)
|
||
case Okular::PageTransition::Blinds:
|
||
{
|
||
const int blinds = isHorizontal ? 8 : 6;
|
||
const int steps = m_width / (4 * blinds);
|
||
if ( isHorizontal )
|
||
{
|
||
int xPosition[ 8 ];
|
||
for ( int b = 0; b < blinds; b++ )
|
||
xPosition[ b ] = (b * m_width) / blinds;
|
||
|
||
for ( int i = 0; i < steps; i++ )
|
||
{
|
||
int stepOffset = (int)( ((float)i * (float)m_width) / ((float)blinds * (float)steps) );
|
||
for ( int b = 0; b < blinds; b++ )
|
||
{
|
||
m_transitionRects.push_back( QRect( xPosition[ b ], 0, stepOffset, m_height ) );
|
||
xPosition[ b ] = stepOffset + (b * m_width) / blinds;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int yPosition[ 6 ];
|
||
for ( int b = 0; b < blinds; b++ )
|
||
yPosition[ b ] = (b * m_height) / blinds;
|
||
|
||
for ( int i = 0; i < steps; i++ )
|
||
{
|
||
int stepOffset = (int)( ((float)i * (float)m_height) / ((float)blinds * (float)steps) );
|
||
for ( int b = 0; b < blinds; b++ )
|
||
{
|
||
m_transitionRects.push_back( QRect( 0, yPosition[ b ], m_width, stepOffset ) );
|
||
yPosition[ b ] = stepOffset + (b * m_height) / blinds;
|
||
}
|
||
}
|
||
}
|
||
m_transitionMul = blinds;
|
||
m_transitionDelay = (int)( (totalTime * 1000) / steps );
|
||
} break;
|
||
|
||
// box: inward / outward
|
||
case Okular::PageTransition::Box:
|
||
{
|
||
const int steps = m_width / 10;
|
||
if ( isInward )
|
||
{
|
||
int L = 0, T = 0, R = m_width, B = m_height;
|
||
for ( int i = 0; i < steps; i++ )
|
||
{
|
||
// compute shrunk box coords
|
||
int newL = ((i + 1) * m_width) / (2 * steps);
|
||
int newT = ((i + 1) * m_height) / (2 * steps);
|
||
int newR = m_width - newL;
|
||
int newB = m_height - newT;
|
||
// add left, right, topcenter, bottomcenter rects
|
||
m_transitionRects.push_back( QRect( L, T, newL - L, B - T ) );
|
||
m_transitionRects.push_back( QRect( newR, T, R - newR, B - T ) );
|
||
m_transitionRects.push_back( QRect( newL, T, newR - newL, newT - T ) );
|
||
m_transitionRects.push_back( QRect( newL, newB, newR - newL, B - newB ) );
|
||
L = newL; T = newT; R = newR, B = newB;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int L = m_width / 2, T = m_height / 2, R = L, B = T;
|
||
for ( int i = 0; i < steps; i++ )
|
||
{
|
||
// compute shrunk box coords
|
||
int newL = ((steps - (i + 1)) * m_width) / (2 * steps);
|
||
int newT = ((steps - (i + 1)) * m_height) / (2 * steps);
|
||
int newR = m_width - newL;
|
||
int newB = m_height - newT;
|
||
// add left, right, topcenter, bottomcenter rects
|
||
m_transitionRects.push_back( QRect( newL, newT, L - newL, newB - newT ) );
|
||
m_transitionRects.push_back( QRect( R, newT, newR - R, newB - newT ) );
|
||
m_transitionRects.push_back( QRect( L, newT, R - L, T - newT ) );
|
||
m_transitionRects.push_back( QRect( L, B, R - L, newB - B ) );
|
||
L = newL; T = newT; R = newR, B = newB;
|
||
}
|
||
}
|
||
m_transitionMul = 4;
|
||
m_transitionDelay = (int)( (totalTime * 1000) / steps );
|
||
} break;
|
||
|
||
// wipe: implemented for 4 canonical angles
|
||
case Okular::PageTransition::Wipe:
|
||
{
|
||
const int angle = transition->angle();
|
||
const int steps = (angle == 0) || (angle == 180) ? m_width / 8 : m_height / 8;
|
||
if ( angle == 0 )
|
||
{
|
||
int xPosition = 0;
|
||
for ( int i = 0; i < steps; i++ )
|
||
{
|
||
int xNext = ((i + 1) * m_width) / steps;
|
||
m_transitionRects.push_back( QRect( xPosition, 0, xNext - xPosition, m_height ) );
|
||
xPosition = xNext;
|
||
}
|
||
}
|
||
else if ( angle == 90 )
|
||
{
|
||
int yPosition = m_height;
|
||
for ( int i = 0; i < steps; i++ )
|
||
{
|
||
int yNext = ((steps - (i + 1)) * m_height) / steps;
|
||
m_transitionRects.push_back( QRect( 0, yNext, m_width, yPosition - yNext ) );
|
||
yPosition = yNext;
|
||
}
|
||
}
|
||
else if ( angle == 180 )
|
||
{
|
||
int xPosition = m_width;
|
||
for ( int i = 0; i < steps; i++ )
|
||
{
|
||
int xNext = ((steps - (i + 1)) * m_width) / steps;
|
||
m_transitionRects.push_back( QRect( xNext, 0, xPosition - xNext, m_height ) );
|
||
xPosition = xNext;
|
||
}
|
||
}
|
||
else if ( angle == 270 )
|
||
{
|
||
int yPosition = 0;
|
||
for ( int i = 0; i < steps; i++ )
|
||
{
|
||
int yNext = ((i + 1) * m_height) / steps;
|
||
m_transitionRects.push_back( QRect( 0, yPosition, m_width, yNext - yPosition ) );
|
||
yPosition = yNext;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
update();
|
||
return;
|
||
}
|
||
m_transitionMul = 1;
|
||
m_transitionDelay = (int)( (totalTime * 1000) / steps );
|
||
} break;
|
||
|
||
// dissolve: replace 'random' rects
|
||
case Okular::PageTransition::Dissolve:
|
||
{
|
||
const int gridXsteps = 50;
|
||
const int gridYsteps = 38;
|
||
const int steps = gridXsteps * gridYsteps;
|
||
int oldX = 0;
|
||
int oldY = 0;
|
||
// create a grid of gridXstep by gridYstep QRects
|
||
for ( int y = 0; y < gridYsteps; y++ )
|
||
{
|
||
int newY = (int)( m_height * ((float)(y+1) / (float)gridYsteps) );
|
||
for ( int x = 0; x < gridXsteps; x++ )
|
||
{
|
||
int newX = (int)( m_width * ((float)(x+1) / (float)gridXsteps) );
|
||
m_transitionRects.push_back( QRect( oldX, oldY, newX - oldX, newY - oldY ) );
|
||
oldX = newX;
|
||
}
|
||
oldX = 0;
|
||
oldY = newY;
|
||
}
|
||
// randomize the grid
|
||
for ( int i = 0; i < steps; i++ )
|
||
{
|
||
#ifndef Q_OS_WIN
|
||
int n1 = (int)(steps * drand48());
|
||
int n2 = (int)(steps * drand48());
|
||
#else
|
||
int n1 = (int)(steps * (std::rand() / RAND_MAX));
|
||
int n2 = (int)(steps * (std::rand() / RAND_MAX));
|
||
#endif
|
||
// swap items if index differs
|
||
if ( n1 != n2 )
|
||
{
|
||
QRect r = m_transitionRects[ n2 ];
|
||
m_transitionRects[ n2 ] = m_transitionRects[ n1 ];
|
||
m_transitionRects[ n1 ] = r;
|
||
}
|
||
}
|
||
// set global transition parameters
|
||
m_transitionMul = 40;
|
||
m_transitionDelay = (int)( (m_transitionMul * 1000 * totalTime) / steps );
|
||
} break;
|
||
|
||
// glitter: similar to dissolve but has a direction
|
||
case Okular::PageTransition::Glitter:
|
||
{
|
||
const int gridXsteps = 50;
|
||
const int gridYsteps = 38;
|
||
const int steps = gridXsteps * gridYsteps;
|
||
const int angle = transition->angle();
|
||
// generate boxes using a given direction
|
||
if ( angle == 90 )
|
||
{
|
||
int yPosition = m_height;
|
||
for ( int i = 0; i < gridYsteps; i++ )
|
||
{
|
||
int yNext = ((gridYsteps - (i + 1)) * m_height) / gridYsteps;
|
||
int xPosition = 0;
|
||
for ( int j = 0; j < gridXsteps; j++ )
|
||
{
|
||
int xNext = ((j + 1) * m_width) / gridXsteps;
|
||
m_transitionRects.push_back( QRect( xPosition, yNext, xNext - xPosition, yPosition - yNext ) );
|
||
xPosition = xNext;
|
||
}
|
||
yPosition = yNext;
|
||
}
|
||
}
|
||
else if ( angle == 180 )
|
||
{
|
||
int xPosition = m_width;
|
||
for ( int i = 0; i < gridXsteps; i++ )
|
||
{
|
||
int xNext = ((gridXsteps - (i + 1)) * m_width) / gridXsteps;
|
||
int yPosition = 0;
|
||
for ( int j = 0; j < gridYsteps; j++ )
|
||
{
|
||
int yNext = ((j + 1) * m_height) / gridYsteps;
|
||
m_transitionRects.push_back( QRect( xNext, yPosition, xPosition - xNext, yNext - yPosition ) );
|
||
yPosition = yNext;
|
||
}
|
||
xPosition = xNext;
|
||
}
|
||
}
|
||
else if ( angle == 270 )
|
||
{
|
||
int yPosition = 0;
|
||
for ( int i = 0; i < gridYsteps; i++ )
|
||
{
|
||
int yNext = ((i + 1) * m_height) / gridYsteps;
|
||
int xPosition = 0;
|
||
for ( int j = 0; j < gridXsteps; j++ )
|
||
{
|
||
int xNext = ((j + 1) * m_width) / gridXsteps;
|
||
m_transitionRects.push_back( QRect( xPosition, yPosition, xNext - xPosition, yNext - yPosition ) );
|
||
xPosition = xNext;
|
||
}
|
||
yPosition = yNext;
|
||
}
|
||
}
|
||
else // if angle is 0 or 315
|
||
{
|
||
int xPosition = 0;
|
||
for ( int i = 0; i < gridXsteps; i++ )
|
||
{
|
||
int xNext = ((i + 1) * m_width) / gridXsteps;
|
||
int yPosition = 0;
|
||
for ( int j = 0; j < gridYsteps; j++ )
|
||
{
|
||
int yNext = ((j + 1) * m_height) / gridYsteps;
|
||
m_transitionRects.push_back( QRect( xPosition, yPosition, xNext - xPosition, yNext - yPosition ) );
|
||
yPosition = yNext;
|
||
}
|
||
xPosition = xNext;
|
||
}
|
||
}
|
||
// add a 'glitter' (1 over 10 pieces is randomized)
|
||
int randomSteps = steps / 20;
|
||
for ( int i = 0; i < randomSteps; i++ )
|
||
{
|
||
#ifndef Q_OS_WIN
|
||
int n1 = (int)(steps * drand48());
|
||
int n2 = (int)(steps * drand48());
|
||
#else
|
||
int n1 = (int)(steps * (std::rand() / RAND_MAX));
|
||
int n2 = (int)(steps * (std::rand() / RAND_MAX));
|
||
#endif
|
||
// swap items if index differs
|
||
if ( n1 != n2 )
|
||
{
|
||
QRect r = m_transitionRects[ n2 ];
|
||
m_transitionRects[ n2 ] = m_transitionRects[ n1 ];
|
||
m_transitionRects[ n1 ] = r;
|
||
}
|
||
}
|
||
// set global transition parameters
|
||
m_transitionMul = (angle == 90) || (angle == 270) ? gridYsteps : gridXsteps;
|
||
m_transitionMul /= 2;
|
||
m_transitionDelay = (int)( (m_transitionMul * 1000 * totalTime) / steps );
|
||
} break;
|
||
|
||
case Okular::PageTransition::Fade:
|
||
{
|
||
enum {FADE_TRANSITION_FPS = 20};
|
||
const int steps = totalTime * FADE_TRANSITION_FPS;
|
||
m_transitionSteps = steps;
|
||
QPainter pixmapPainter;
|
||
m_currentPixmapOpacity = (double) 1 / steps;
|
||
m_transitionDelay = (int)( totalTime * 1000 ) / steps;
|
||
m_lastRenderedPixmap = QPixmap( m_lastRenderedPixmap.size() );
|
||
m_lastRenderedPixmap.fill( Qt::transparent );
|
||
pixmapPainter.begin( &m_lastRenderedPixmap );
|
||
pixmapPainter.setCompositionMode( QPainter::CompositionMode_Source );
|
||
pixmapPainter.setOpacity( 1 - m_currentPixmapOpacity );
|
||
pixmapPainter.drawPixmap( 0, 0, m_previousPagePixmap );
|
||
pixmapPainter.setOpacity( m_currentPixmapOpacity );
|
||
pixmapPainter.drawPixmap( 0, 0, m_currentPagePixmap );
|
||
pixmapPainter.end();
|
||
update();
|
||
} break;
|
||
// implement missing transitions (a binary raster engine needed here)
|
||
case Okular::PageTransition::Fly:
|
||
|
||
case Okular::PageTransition::Push:
|
||
|
||
case Okular::PageTransition::Cover:
|
||
|
||
case Okular::PageTransition::Uncover:
|
||
|
||
default:
|
||
update();
|
||
return;
|
||
}
|
||
|
||
// send the first start to the timer
|
||
m_transitionTimer->start( 0 );
|
||
}
|
||
|
||
void PresentationWidget::slotProcessMovieAction( const Okular::MovieAction *action )
|
||
{
|
||
const Okular::MovieAnnotation *movieAnnotation = action->annotation();
|
||
if ( !movieAnnotation )
|
||
return;
|
||
|
||
Okular::Movie *movie = movieAnnotation->movie();
|
||
if ( !movie )
|
||
return;
|
||
|
||
VideoWidget *vw = m_frames[ m_frameIndex ]->videoWidgets.value( movieAnnotation->movie() );
|
||
if ( !vw )
|
||
return;
|
||
|
||
vw->show();
|
||
|
||
switch ( action->operation() )
|
||
{
|
||
case Okular::MovieAction::Play:
|
||
vw->stop();
|
||
vw->play();
|
||
break;
|
||
case Okular::MovieAction::Stop:
|
||
vw->stop();
|
||
break;
|
||
case Okular::MovieAction::Pause:
|
||
vw->pause();
|
||
break;
|
||
case Okular::MovieAction::Resume:
|
||
vw->play();
|
||
break;
|
||
};
|
||
}
|
||
|
||
void PresentationWidget::slotProcessRenditionAction( const Okular::RenditionAction *action )
|
||
{
|
||
Okular::Movie *movie = action->movie();
|
||
if ( !movie )
|
||
return;
|
||
|
||
VideoWidget *vw = m_frames[ m_frameIndex ]->videoWidgets.value( movie );
|
||
if ( !vw )
|
||
return;
|
||
|
||
if ( action->operation() == Okular::RenditionAction::None )
|
||
return;
|
||
|
||
vw->show();
|
||
|
||
switch ( action->operation() )
|
||
{
|
||
case Okular::RenditionAction::Play:
|
||
vw->stop();
|
||
vw->play();
|
||
break;
|
||
case Okular::RenditionAction::Stop:
|
||
vw->stop();
|
||
break;
|
||
case Okular::RenditionAction::Pause:
|
||
vw->pause();
|
||
break;
|
||
case Okular::RenditionAction::Resume:
|
||
vw->play();
|
||
break;
|
||
default:
|
||
return;
|
||
};
|
||
}
|
||
|
||
void PresentationWidget::slotTogglePlayPause()
|
||
{
|
||
m_advanceSlides = !m_advanceSlides;
|
||
setPlayPauseIcon();
|
||
if ( m_advanceSlides )
|
||
{
|
||
startAutoChangeTimer();
|
||
}
|
||
else
|
||
{
|
||
m_nextPageTimer->stop();
|
||
}
|
||
}
|
||
|
||
#include "presentationwidget.moc"
|