File indexing completed on 2024-05-12 16:36:42

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2007-2008 Thorsten Zachmann <zachmann@kde.org>
0003  * Copyright (C) 2010 Benjamin Port <port.benjamin@gmail.com>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Library General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2 of the License, or (  at your option ) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, write to
0017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019  */
0020 
0021 #include "KPrAnimationDirector.h"
0022 
0023 #include <QList>
0024 #include <QPainter>
0025 #include <QPaintEvent>
0026 #include <QWidget>
0027 #include <QVariant>
0028 #include <QApplication>
0029 
0030 #include <KoPageLayout.h>
0031 #include <KoShapeManager.h>
0032 #include <KoShapeManagerPaintingStrategy.h>
0033 #include <KoShapePaintingContext.h>
0034 #include <KoViewConverter.h>
0035 #include <KoPAViewMode.h>
0036 #include <KoPACanvas.h>
0037 #include <KoPAPageBase.h>
0038 #include <KoPAView.h>
0039 #include <KoPAUtil.h>
0040 #include <KoShapeLayer.h>
0041 #include <KoPAMasterPage.h>
0042 #include <KoSelection.h>
0043 
0044 #include "KPrEndOfSlideShowPage.h"
0045 #include "KPrPage.h"
0046 #include "KPrMasterPage.h"
0047 #include "KPrPageApplicationData.h"
0048 #include "KPrShapeManagerAnimationStrategy.h"
0049 #include "KPrShapeManagerDisplayMasterStrategy.h"
0050 #include "KPrPageSelectStrategyActive.h"
0051 #include "pageeffects/KPrPageEffectRunner.h"
0052 #include "pageeffects/KPrPageEffect.h"
0053 #include "KPrShapeAnimations.h"
0054 #include "KPrPageTransition.h"
0055 #include "StageDebug.h"
0056 
0057 #include "animations/KPrAnimationCache.h"
0058 
0059 
0060 KPrAnimationDirector::KPrAnimationDirector( KoPAView * view, KoPACanvas * canvas, const QList<KoPAPageBase*> & pages, KoPAPageBase* currentPage )
0061 : m_view( view )
0062 , m_canvas( canvas )
0063 , m_pages( pages )
0064 , m_pageEffectRunner( 0 )
0065 , m_stepIndex( 0 )
0066 , m_maxShapeDuration( 0 )
0067 , m_hasAnimation( false )
0068 , m_animationCache( 0 )
0069 , m_state(PresentationState)
0070 {
0071     Q_ASSERT( !m_pages.empty() );
0072     m_animationCache = new KPrAnimationCache();
0073     if( !currentPage || !pages.contains(currentPage))
0074         updateActivePage( m_pages[0] );
0075     else
0076         updateActivePage( currentPage );
0077 
0078     m_pageIndex = m_pages.indexOf( m_view->activePage() );
0079 
0080     // updatePageAnimation was called from updateZoom() [updateActivePage()]
0081 
0082     connect( &m_timeLine, SIGNAL(valueChanged(qreal)), this, SLOT(animate()) );
0083     // this is needed as after a call to m_canvas->showFullScreen the canvas is not made fullscreen right away
0084     connect( m_canvas, SIGNAL(sizeChanged(QSize)), this, SLOT(updateZoom(QSize)) );
0085     m_timeLine.setCurveShape( QTimeLine::LinearCurve );
0086     m_timeLine.setUpdateInterval( 20 );
0087     // set the animation strategy in the KoShapeManagers
0088     m_canvas->shapeManager()->setPaintingStrategy( new KPrShapeManagerAnimationStrategy( m_canvas->shapeManager(), m_animationCache,
0089                                                        new KPrPageSelectStrategyActive( m_view->kopaCanvas() ) ) );
0090     m_canvas->masterShapeManager()->setPaintingStrategy( new KPrShapeManagerAnimationStrategy( m_canvas->masterShapeManager(), m_animationCache,
0091                                                              new KPrPageSelectStrategyActive( m_view->kopaCanvas() ) ) );
0092 
0093 
0094     m_autoTransitionTimer.setSingleShot(true);
0095     connect(&m_autoTransitionTimer, SIGNAL(timeout()), this, SLOT(nextPage()));
0096     connect(&m_timeLine, SIGNAL(finished()), this, SLOT(slotTimelineFinished()));
0097     if (hasAutoSlideTransition()) {
0098         if (hasPageEffect() || hasAnimation()) {
0099             nextStep();
0100         } else {
0101             startAutoSlideTransition();
0102         }
0103     }
0104 }
0105 
0106 KPrAnimationDirector::~KPrAnimationDirector()
0107 {
0108     // free used resources
0109     delete m_pageEffectRunner;
0110     delete m_animationCache;
0111     //set the KoShapeManagerPaintingStrategy in the KoShapeManagers
0112     m_canvas->shapeManager()->setPaintingStrategy( new KoShapeManagerPaintingStrategy( m_canvas->shapeManager() ) );
0113     m_canvas->masterShapeManager()->setPaintingStrategy( new KPrShapeManagerDisplayMasterStrategy( m_canvas->masterShapeManager(),
0114                                                              new KPrPageSelectStrategyActive( m_view->kopaCanvas() ) ) );
0115 }
0116 
0117 
0118 
0119 void KPrAnimationDirector::paint(QPainter& painter, const QRectF &paintRect)
0120 {
0121     if ( m_pageEffectRunner )
0122     {
0123         bool finished = m_pageEffectRunner->isFinished();
0124         if ( !m_pageEffectRunner->paint( painter ) )
0125         {
0126             delete m_pageEffectRunner;
0127             m_pageEffectRunner = 0;
0128 
0129             // check if there where a animation to start
0130             if ( hasAnimation() ) {
0131                 if ( finished ) {
0132                     QRect clipRect = m_pageRect.intersected( paintRect.toRect() );
0133                     painter.setClipRect( clipRect );
0134                     painter.setRenderHint( QPainter::Antialiasing );
0135                     paintStep( painter );
0136                 }
0137                 else {
0138                     // start the animations
0139                     startTimeLine( m_animations.at(m_stepIndex)->totalDuration() );
0140                 }
0141             }
0142         }
0143     }
0144     else {
0145         QRect clipRect = m_pageRect.intersected( paintRect.toRect() );
0146         painter.setClipRect( clipRect );
0147         painter.setRenderHint( QPainter::Antialiasing );
0148         paintStep( painter );
0149     }
0150 }
0151 
0152 void KPrAnimationDirector::paintEvent( QPaintEvent* event )
0153 {
0154     QPainter painter( m_canvas );
0155     paint(painter, event->rect());
0156 }
0157 
0158 KoViewConverter * KPrAnimationDirector::viewConverter()
0159 {
0160     return &m_zoomHandler;
0161 }
0162 
0163 int KPrAnimationDirector::numPages() const
0164 {
0165     return m_pages.size();
0166 }
0167 
0168 KoPAPageBase *KPrAnimationDirector::page(int index) const
0169 {
0170     return m_pages.value(index);
0171 }
0172 
0173 int KPrAnimationDirector::currentPage() const
0174 {
0175     return m_pageIndex;
0176 }
0177 
0178 int KPrAnimationDirector::numStepsInPage() const
0179 {
0180     return m_animations.size();
0181 }
0182 
0183 int KPrAnimationDirector::currentStep() const
0184 {
0185     return m_stepIndex;
0186 }
0187 
0188 bool KPrAnimationDirector::navigate( Navigation navigation )
0189 {
0190     bool finished = false;
0191     if ( m_pageEffectRunner ) {
0192         m_pageEffectRunner->finish();
0193         finishAnimations();
0194         // finish on first step
0195         m_timeLine.stop();
0196         finished = true;
0197     }
0198     else if ( m_timeLine.state() == QTimeLine::Running ) { // there are still shape animations running
0199         finishAnimations();
0200         m_timeLine.stop();
0201         finished = true;
0202     }
0203 
0204     bool presentationFinished = false;
0205 
0206     switch ( navigation )
0207     {
0208         case FirstPage:
0209         case PreviousPage:
0210         case NextPage:
0211         case LastPage:
0212             presentationFinished = changePage( navigation );
0213             break;
0214         case PreviousStep:
0215             previousStep();
0216             break;
0217         case NextStep:
0218             if ( !finished ) {
0219                 presentationFinished = nextStep();
0220             }
0221             break;
0222         default:
0223             break;
0224     }
0225 
0226     return presentationFinished;
0227 }
0228 
0229 void KPrAnimationDirector::navigateToPage( int index )
0230 {
0231     if ( m_pageEffectRunner ) {
0232         m_pageEffectRunner->finish();
0233         finishAnimations();
0234         // finish on first step
0235         m_timeLine.stop();
0236     }
0237     else if ( m_timeLine.state() == QTimeLine::Running ) { // there are still shape animations running
0238         finishAnimations();
0239         m_timeLine.stop();
0240     }
0241 
0242     m_pageIndex = index;
0243     KoPAPageBase *page = m_pages[m_pageIndex];
0244 
0245     m_stepIndex = 0;
0246 
0247     updateActivePage( page );
0248     updatePageAnimation();
0249     updateStepAnimation();
0250     // trigger a repaint
0251     m_canvas->update();
0252 }
0253 
0254 void KPrAnimationDirector::updateActivePage( KoPAPageBase * page )
0255 {
0256     deactivate();
0257 
0258     if ( m_canvas == m_view->kopaCanvas() ) {
0259         m_view->viewMode()->updateActivePage( page );
0260     }
0261     else {
0262         QList<KoShape*> shapes = page->shapes();
0263         m_canvas->shapeManager()->setShapes(shapes, KoShapeManager::AddWithoutRepaint);
0264         //Make the top most layer active
0265         if ( !shapes.isEmpty() ) {
0266             KoShapeLayer* layer = dynamic_cast<KoShapeLayer*>( shapes.last() );
0267             m_canvas->shapeManager()->selection()->setActiveLayer( layer );
0268         }
0269 
0270         // if the page is not a master page itself set shapes of the master page
0271         KoPAPage * paPage = dynamic_cast<KoPAPage *>( page );
0272 
0273         Q_ASSERT( paPage );
0274         KoPAMasterPage * masterPage = paPage->masterPage();
0275         QList<KoShape*> masterShapes = masterPage->shapes();
0276         m_canvas->masterShapeManager()->setShapes(masterShapes, KoShapeManager::AddWithoutRepaint);
0277         // Make the top most layer active
0278         if ( !masterShapes.isEmpty() ) {
0279             KoShapeLayer* layer = dynamic_cast<KoShapeLayer*>( masterShapes.last() );
0280             m_canvas->masterShapeManager()->selection()->setActiveLayer( layer );
0281         }
0282     }
0283 
0284     KPrPage * kprPage = dynamic_cast<KPrPage *>( page );
0285     Q_ASSERT( kprPage );
0286     if (m_pageIndex > m_pages.size() || m_pageIndex < 0) {
0287         m_pageIndex = m_pages.indexOf(page);
0288     }
0289     m_animations = kprPage->animations().steps();
0290 
0291     // it can be that the pages have different sizes. So we need to recalculate
0292     // the zoom when we change the page
0293     updateZoom( m_canvas->size() );
0294 }
0295 
0296 void KPrAnimationDirector::updatePageAnimation()
0297 {
0298     m_animationCache->clear();
0299 
0300     m_animationCache->setPageSize(m_pages[m_pageIndex]->size());
0301     qreal zoom;
0302     m_zoomHandler.zoom(&zoom, &zoom);
0303     m_animationCache->setZoom(zoom);
0304     int i = 0;
0305     foreach (KPrAnimationStep *step, m_animations) {
0306         step->init(m_animationCache, i);
0307         i++;
0308     }
0309 }
0310 
0311 void KPrAnimationDirector::updateStepAnimation()
0312 {
0313     m_animationCache->startStep(m_stepIndex);
0314 }
0315 
0316 
0317 bool KPrAnimationDirector::changePage( Navigation navigation )
0318 {
0319     switch ( navigation )
0320     {
0321         case FirstPage:
0322             m_pageIndex = 0;
0323             break;
0324         case PreviousPage:
0325             m_pageIndex = m_pageIndex > 0 ? m_pageIndex - 1 : 0;
0326             break;
0327         case NextPage:
0328             if ( m_pageIndex < m_pages.size() -1 ) {
0329                 ++m_pageIndex;
0330             }
0331             else {
0332                 return true;
0333             }
0334             break;
0335         case LastPage:
0336             m_pageIndex = m_pages.size() - 1;
0337             if ( dynamic_cast<KPrEndOfSlideShowPage *>( m_pages[m_pageIndex] ) && m_pageIndex > 0 ) {
0338                 m_pageIndex--;
0339             }
0340             break;
0341         case PreviousStep:
0342         case NextStep:
0343         default:
0344             // this should not happen
0345             Q_ASSERT( 0 );
0346             break;
0347     }
0348     m_stepIndex = 0;
0349 
0350     updateActivePage( m_pages[m_pageIndex] );
0351     updatePageAnimation();
0352     updateStepAnimation();
0353 
0354     // trigger a repaint
0355     m_canvas->update();
0356 
0357     return false;
0358 }
0359 
0360 void KPrAnimationDirector::updateZoom( const QSize & size )
0361 {
0362     KoPageLayout pageLayout = m_view->activePage()->pageLayout();
0363     KoPAUtil::setZoom( pageLayout, size, m_zoomHandler );
0364     m_pageRect = KoPAUtil::pageRect( pageLayout, size, m_zoomHandler );
0365     m_canvas->setDocumentOffset( -m_pageRect.topLeft() );
0366 
0367     // reinit page animation, because somi init method contain zoom
0368     updatePageAnimation();
0369     updateStepAnimation();
0370 }
0371 
0372 void KPrAnimationDirector::paintStep( QPainter & painter )
0373 {
0374     if (m_pageRect != m_canvas->rect()) {
0375         painter.setClipping(false);
0376         painter.fillRect(m_canvas->rect(), Qt::black);
0377         painter.setClipping(true);
0378     }
0379     painter.translate( m_pageRect.topLeft() );
0380     KoShapePaintingContext context;
0381     m_view->activePage()->paintBackground( painter, m_zoomHandler, context );
0382 
0383     if ( m_view->activePage()->displayMasterShapes() ) {
0384         foreach ( KoShape *shape, m_canvas->masterShapeManager()->shapes() ) {
0385             shape->waitUntilReady( m_zoomHandler, false );
0386         }
0387 
0388         m_canvas->masterShapeManager()->paint( painter, m_zoomHandler, true );
0389     }
0390     foreach ( KoShape *shape, m_canvas->shapeManager()->shapes() ) {
0391         shape->waitUntilReady( m_zoomHandler, false );
0392     }
0393     m_canvas->shapeManager()->paint( painter, m_zoomHandler, true );
0394 }
0395 
0396 bool KPrAnimationDirector::nextStep()
0397 {
0398     if ( m_stepIndex < numStepsInPage() - 1 ) {
0399         // if there are sub steps go to the next substep
0400         ++m_stepIndex;
0401         m_state = EntryAnimationState;
0402         updateStepAnimation();
0403         startTimeLine(m_animations.at(m_stepIndex)->totalDuration());
0404     }
0405     else {
0406         // if there are no more sub steps go to the next page
0407         // The active page and the substeps are updated later as
0408         // first the current page has to be painted again for the page effect
0409         if ( m_pageIndex < m_pages.size() -1 ) {
0410             ++m_pageIndex;
0411         }
0412         else {
0413             return true;
0414         }
0415         m_stepIndex = 0;
0416 
0417         KPrPageEffect * effect = KPrPage::pageData( m_pages[m_pageIndex] )->pageEffect();
0418 
0419         // run page effect if there is one
0420         if ( effect ) {
0421             QPixmap oldPage( m_canvas->size() );
0422             m_canvas->render( &oldPage );
0423 
0424             updateActivePage( m_pages[m_pageIndex] );
0425             updatePageAnimation();
0426             updateStepAnimation();
0427             QPixmap newPage( m_canvas->size() );
0428             newPage.fill( Qt::white ); // TODO
0429             QPainter newPainter( &newPage );
0430             newPainter.setClipRect( m_pageRect );
0431             newPainter.setRenderHint( QPainter::Antialiasing );
0432             paintStep( newPainter );
0433 
0434             m_state = EntryEffectState;
0435             m_pageEffectRunner = new KPrPageEffectRunner( oldPage, newPage, m_canvas, effect );
0436             startTimeLine( effect->duration() );
0437         }
0438         else {
0439             updateActivePage( m_pages[m_pageIndex] );
0440             updatePageAnimation();
0441             updateStepAnimation();
0442             m_canvas->update();
0443             if ( hasAnimation() ) {
0444                 m_state = EntryAnimationState;
0445                 startTimeLine( m_animations.at(m_stepIndex)->totalDuration() );
0446             } else if (hasAutoSlideTransition()) {
0447                 m_state = PresentationState;
0448                 startAutoSlideTransition();
0449             } else {
0450                 m_state = PresentationState;
0451             }
0452         }
0453     }
0454     return false;
0455 }
0456 
0457 void KPrAnimationDirector::nextPage()
0458 {
0459     nextStep();
0460 }
0461 
0462 void KPrAnimationDirector::previousStep()
0463 {
0464     if ( m_stepIndex > 0 ) {
0465         --m_stepIndex;
0466     }
0467     else {
0468         if ( m_pageIndex > 0 ) {
0469             --m_pageIndex;
0470             updateActivePage( m_pages[m_pageIndex] );
0471             if(hasAnimation()) {
0472                 m_stepIndex = m_animations.size() - 1;
0473             }
0474             else {
0475                 m_stepIndex = m_animations.size();
0476             }
0477             updatePageAnimation();
0478             // trigger repaint
0479             m_canvas->update();
0480             // cancel a running page effect
0481             delete m_pageEffectRunner;
0482             m_pageEffectRunner = 0;
0483         }
0484     }
0485     // when going back you always go to the end of the effect
0486     finishAnimations();
0487 }
0488 
0489 bool KPrAnimationDirector::hasAutoSlideTransition() const
0490 {
0491     return KPrPage::pageData( m_pages[m_pageIndex] )->pageTransition().type() == KPrPageTransition::Automatic;
0492 }
0493 
0494 void KPrAnimationDirector::startAutoSlideTransition()
0495 {
0496     m_autoTransitionTimer.start(KPrPage::pageData( m_pages[m_pageIndex] )->pageTransition().milliseconds());
0497 }
0498 
0499 bool KPrAnimationDirector::pageEffectRunning() const
0500 {
0501     return m_pageEffectRunner;
0502 }
0503 
0504 bool KPrAnimationDirector::hasPageEffect() const
0505 {
0506     return KPrPage::pageData( m_pages[m_pageIndex] )->pageEffect() != nullptr;
0507 }
0508 
0509 bool KPrAnimationDirector::animationRunning() const
0510 {
0511     return hasAnimation() && m_timeLine.state() != QTimeLine::NotRunning;
0512 }
0513 
0514 bool KPrAnimationDirector::hasAnimation() const
0515 {
0516     return m_animations.size() > 0;
0517 }
0518 bool KPrAnimationDirector::moreAnimationSteps() const
0519 {
0520     return m_stepIndex < m_animations.size() - 1;
0521 }
0522 
0523 void KPrAnimationDirector::animate()
0524 {
0525     if ( m_pageEffectRunner ) {
0526         m_pageEffectRunner->next( m_timeLine.currentTime() );
0527     }
0528     else if ( hasAnimation() ) { //if there are animations
0529         // set current time, to the current step
0530         m_animationCache->next();
0531         m_animations.at(m_stepIndex)->setCurrentTime(m_timeLine.currentTime());
0532         m_canvas->update();
0533     }
0534 }
0535 
0536 void KPrAnimationDirector::finishAnimations()
0537 {
0538     m_animationCache->endStep(m_stepIndex);
0539     m_canvas->update();
0540     m_state = PresentationState;
0541 }
0542 
0543 void KPrAnimationDirector::startTimeLine( int duration )
0544 {
0545     if (duration == 0) {
0546         m_timeLine.setDuration( 1 );
0547     }
0548     else {
0549        m_timeLine.setDuration( duration );
0550     }
0551     m_timeLine.setCurrentTime( 0 );
0552     m_timeLine.start();
0553 }
0554 
0555 void KPrAnimationDirector::slotTimelineFinished()
0556 {
0557     switch (m_state) {
0558         case PresentationState:
0559             break;
0560         case EntryEffectState:
0561             if (hasAutoSlideTransition()) {
0562                 if (hasAnimation()) {
0563                     nextStep();
0564                 } else {
0565                     m_state = PresentationState;
0566                     startAutoSlideTransition();
0567                 }
0568             } else {
0569                 m_state = PresentationState;
0570             }
0571             break;
0572         case EntryAnimationState:
0573             if (hasAutoSlideTransition()) {
0574                 if (moreAnimationSteps()) {
0575                     nextStep();
0576                 } else if (hasAutoSlideTransition()) {
0577                     m_state = PresentationState;
0578                     startAutoSlideTransition();
0579                 } else {
0580                     m_state = PresentationState;
0581                 }
0582             } else {
0583                 m_state = PresentationState;
0584             }
0585             break;
0586     }
0587 }
0588 
0589 void KPrAnimationDirector::deactivate()
0590 {
0591     m_state = PresentationState;
0592     m_autoTransitionTimer.stop();
0593     foreach (KPrAnimationStep *step, m_animations) {
0594         step->deactivate();
0595     }
0596 }