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 }