/** During animations the update() method tells its caller to call it as soonaspossible.Thisgivesusmoretimetorenderthenextframeand stillmaintainasteadyframerate.Thisclassisresponsiblefor synchronizingthedisplayofnewframesandthuskeepingtheframerate steady.
*/ class FrameSynchronization
{ public: /** Create new object with a predefined duration between two frames. @paramnFrameDuration Thepreferreddurationbetweenthedisplayoftwoframesin seconds.
*/ explicit FrameSynchronization (constdouble nFrameDuration);
/** Set the current time as the time at which the current frame is displayed.Fromthisthetargettimeofthenextframeisderived.
*/ void MarkCurrentFrame();
/** When there is time left until the next frame is due then wait. Otherwisereturnwithoutdelay.
*/ void Synchronize();
/** Activate frame synchronization when an animation is active and framesaretobedisplayedinasteadyrate.Whileactive Synchronize()willwaituntiltheframedurationtimehaspassed.
*/ void Activate();
/** Deactivate frame synchronization when no animation is active and the timebetweenframesdependsonuseractionsandotherexternal sources.WhiledeactivatedSynchronize()willreturnwithoutdelay.
*/ void Deactivate();
private: /** The timer that is used for synchronization is independent from the oneusedbySlideShowImpl:itisnotpausedormodifiedby animations.
*/
canvas::tools::ElapsedTime maTimer; /** Time between the display of frames. Enforced only when mbIsActive is<TRUE/>.
*/ constdouble mnFrameDuration; /** Time (of maTimer) when the next frame shall be displayed. Synchronize()willwaituntilthistime.
*/ double mnNextFrameTargetTime; /** Synchronize() will wait only when this flag is <TRUE/>. Otherwise itreturnsimmediately.
*/ bool mbIsActive;
};
class SlideShowImpl : private cppu::BaseMutex, public CursorManager, public MediaFileManager, public SlideShowImplBase
{ public: explicit SlideShowImpl(
uno::Reference<uno::XComponentContext> xContext );
/** Notify that the transition phase of the current slide hasended.
/** Notification from eventmultiplexer that a hyperlink hasbeenclicked.
*/ bool notifyHyperLinkClicked( OUString const& hyperLink );
/** Notification from eventmultiplexer that an animation event has occurred. ThiswillbeforwardedtoallregisteredXSlideShowListener
*/ bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode );
/** Obtain a MediaTempFile for the specified url. */ virtual std::shared_ptr<avmedia::MediaTempFile> getMediaTempFile(const OUString& aUrl) override;
/** This is somewhat similar to displaySlide when called for the current slide.Ithasbeensimplifiedtotakeadvantageofthatnoslide changetakesplace.Furthermoreitdoesnotshowtheslide transition.
*/ void redisplayCurrentSlide();
/// Resets the current slide transition sound object with a new one:
SoundPlayerSharedPtr resetSlideTransitionSound(
uno::Any const& url, bool bLoopSound );
/// stops the current slide transition sound void stopSlideTransitionSound();
/** Request/release the wait symbol. The wait symbol is displayed when therearemorerequeststhenreleases.Lockingthewaitsymbol helpstoavoidintermediaterepaints.
/// Filter requested cursor shape against hard slideshow cursors (wait, etc.)
sal_Int16 calcActiveCursor( sal_Int16 nCursorShape ) const;
/** This method is called asynchronously to finish the rewinding of an effecttothepreviousslidethatwasinitiatedearlier.
*/ void rewindEffectToPreviousSlide();
/// all registered views
UnoViewContainer maViewContainer;
/// all registered slide show listeners
comphelper::OInterfaceContainerHelper3<presentation::XSlideShowListener> maListenerContainer;
/// map of vectors, containing all registered listeners for a shape
ShapeEventListenerMap maShapeEventListeners; /// map of sal_Int16 values, specifying the mouse cursor for every shape
ShapeCursorMap maShapeCursors;
//map of vector of Polygons, containing polygons drawn on each slide.
PolygonMap maPolygons;
std::optional<RGBColor> maUserPaintColor;
double maUserPaintStrokeWidth;
//changed for the eraser project
std::optional<bool> maEraseAllInk;
std::optional<sal_Int32> maEraseInk; //end changed
/// the previously running slide
SlideSharedPtr mpPreviousSlide; /// the currently running slide
SlideSharedPtr mpCurrentSlide; /// the already prefetched slide: best candidate for upcoming slide
SlideSharedPtr mpPrefetchSlide; /// slide to be prefetched: best candidate for upcoming slide
uno::Reference<drawing::XDrawPage> mxPrefetchSlide; /// save the XDrawPagesSupplier to retrieve polygons
uno::Reference<drawing::XDrawPagesSupplier> mxDrawPagesSupplier; /// Used by MediaFileManager, for media files with package url.
uno::Reference<document::XStorageBasedDocument> mxSBD; /// slide animation to be prefetched:
uno::Reference<animations::XAnimationNode> mxPrefetchAnimationNode;
// EventHandler virtualbool handleEvent() override
{ // DON't call notifySlideAnimationsEnded() // directly, but queue an event. handleEvent() // might be called from e.g. // showNext(), and notifySlideAnimationsEnded() must not be called // in recursion. Note that the event is scheduled for the next // frame so that its expensive execution does not come in between // sprite hiding and shape redraw (at the end of the animation of a // shape), which would cause a flicker.
mrEventQueue.addEventForNextRound(
makeEvent( [this] () { this->mrShow.notifySlideAnimationsEnded(); },
u"SlideShowImpl::notifySlideAnimationsEnded"_ustr)); returntrue;
}
if (!url.isEmpty())
{ try
{
mpCurrentSlideTransitionSound = SoundPlayer::create(
maEventMultiplexer, url, mxComponentContext, *this);
mpCurrentSlideTransitionSound->setPlaybackLoop( bLoopSound );
} catch (lang::NoSupportException const&)
{ // catch possible exceptions from SoundPlayer, since // being not able to playback the sound is not a hard // error here (still, the slide transition should be // shown).
}
} return mpCurrentSlideTransitionSound;
}
if( !getPropertyValue( aSound, xPropSet, u"Sound"_ustr) )
SAL_INFO("slideshow", "createSlideTransition(): Could not determine transition sound effect URL from XDrawPage - using no sound" );
if( !getPropertyValue( bLoopSound, xPropSet, u"LoopSound"_ustr ) )
SAL_INFO("slideshow", "createSlideTransition(): Could not get slide property 'LoopSound' - using no sound" );
if( !pTransition ) return ActivitySharedPtr(); // no transition effect has been // generated. Normally, that means // that simply no transition is // set on this slide.
double nTransitionDuration(0.0); if( !getPropertyValue( nTransitionDuration,
xPropSet,
u"TransitionDuration"_ustr) )
{
SAL_INFO("slideshow", "createSlideTransition(): " "Could not extract slide transition duration from XDrawPage - assuming no transition" ); return ActivitySharedPtr();
}
sal_Int32 nMinFrames(5); if( !getPropertyValue( nMinFrames,
xPropSet,
u"MinimalFrameNumber"_ustr) )
{
SAL_INFO("slideshow", "createSlideTransition(): " "No minimal number of frames given - assuming 5" );
}
// prefetch slide transition bitmaps, but postpone it after // displaySlide() has finished - sometimes, view size has not yet // reached final size
maEventQueue.addEvent(
makeEvent( [pTransition] () {
pTransition->prefetch(); },
u"Animation::prefetch"_ustr));
if (mnWaitSymbolRequestCount == 1)
{ if( !mpWaitSymbol )
{ // fall back to cursor
requestCursor(calcActiveCursor(mnCurrentCursor));
} else
mpWaitSymbol->show();
}
}
if (mnWaitSymbolRequestCount == 0)
{ if( !mpWaitSymbol )
{ // fall back to cursor
requestCursor(calcActiveCursor(mnCurrentCursor));
} else
mpWaitSymbol->hide();
}
}
// clear all queues
maEventQueue.clear();
maActivitiesQueue.clear();
// Attention: we MUST clear the user event queue here, // this is because the current slide might have registered // shape events (click or enter/leave), which might // otherwise dangle forever in the queue (because of the // shared ptr nature). If someone needs to change this: // somehow unregister those shapes at the user event queue // on notifySlideEnded().
maUserEventQueue.clear();
// re-enable automatic effect advancement // (maEventQueue.clear() above might have killed // maEventMultiplexer's tick events) if (mbAutomaticAdvancementMode)
{ // toggle automatic mode (enabling just again is // ignored by EventMultiplexer)
maEventMultiplexer.setAutomaticMode( false );
maEventMultiplexer.setAutomaticMode( true );
}
}
stopShow(); // MUST call that: results in // maUserEventQueue.clear(). What's more, // stopShow()'s currSlide->hide() call is // now also required, notifySlideEnded() // relies on that // unconditionally. Otherwise, genuine // shape animations (drawing layer and // GIF) will not be stopped.
// push new transformation to all views, if size changed if( !mpPreviousSlide || oldSlideSize != slideSize )
{ for( constauto& pView : maViewContainer )
pView->setViewSize( slideSize );
// explicitly notify view change here, // because transformation might have changed: // optimization, this->notifyViewChange() would // repaint slide which is not necessary.
maEventMultiplexer.notifyViewsChanged();
}
// create slide transition, and add proper end event // (which then starts the slide effects // via CURRENT_SLIDE.show())
ActivitySharedPtr pSlideChangeActivity (
createSlideTransition(
mpCurrentSlide->getXDrawPage(),
mpPreviousSlide,
mpCurrentSlide,
makeEvent(
[this] () { this->notifySlideTransitionEnded(false); },
u"SlideShowImpl::notifySlideTransitionEnded"_ustr)));
if (bSkipSlideTransition)
{ // The transition activity was created for the side effects // (like sound transitions). Because we want to skip the // actual transition animation we do not need the activity // anymore.
pSlideChangeActivity.reset();
}
if (pSlideChangeActivity)
{ // factory generated a slide transition - activate it!
maActivitiesQueue.addActivity( pSlideChangeActivity );
} else
{ // no transition effect on this slide - schedule slide // effect start event right away.
maEventQueue.addEvent(
makeEvent(
[this] () { this->notifySlideTransitionEnded(true); },
u"SlideShowImpl::notifySlideTransitionEnded"_ustr));
}
}
} // finally
// We are currently rewinding an effect. This lead us from the next // slide to this one. To complete this we have to play back all main // sequence effects on this slide. if (bSkipAllMainSequenceEffects)
maEffectRewinder.skipAllMainSequenceEffects();
}
void SlideShowImpl::rewindEffectToPreviousSlide()
{ // Show the wait symbol now and prevent it from showing temporary slide // content while effects are played back.
WaitSymbolLock aLock (*this);
// A previous call to EffectRewinder::Rewind could not rewind the current // effect because there are no effects on the current slide or none has // yet been displayed. Go to the previous slide.
notifySlideEnded(true);
// Process pending events once more in order to have the following // screen update show the last effect. Not sure whether this should be // necessary.
maEventQueue.forceEmpty();
// We have to call the screen updater before the wait symbol is turned // off. Otherwise the wait symbol would force the display of an // intermediate state of the slide (before the effects are replayed.)
maScreenUpdater.commitUpdates();
}
sal_Bool SlideShowImpl::startShapeActivity(
uno::Reference<drawing::XShape> const& /*xShape*/ )
{ // precondition: must only be called from the main thread!
DBG_TESTSOLARMUTEX();
sal_Bool SlideShowImpl::stopShapeActivity(
uno::Reference<drawing::XShape> const& /*xShape*/ )
{ // precondition: must only be called from the main thread!
DBG_TESTSOLARMUTEX();
// precondition: must only be called from the main thread!
DBG_TESTSOLARMUTEX();
// first of all, check if view has a valid canvas
ENSURE_OR_RETURN_FALSE( xView.is(), "addView(): Invalid view" );
ENSURE_OR_RETURN_FALSE( xView->getCanvas().is(), "addView(): View does not provide a valid canvas" );
drawing::PointSequenceSequence
lcl_createPointSequenceSequenceFromB2DPolygon(const basegfx::B2DPolygon& rPoly)
{
drawing::PointSequenceSequence aRetval; //Create only one sequence for one pen drawing.
aRetval.realloc(1); // Retrieve the sequence of points from aRetval
drawing::PointSequence* pOuterSequence = aRetval.getArray(); // Create points in this sequence from rPoly
pOuterSequence->realloc(rPoly.count()); // Get these points which are in an array
awt::Point* pInnerSequence = pOuterSequence->getArray(); for( sal_uInt32 n = 0; n < rPoly.count(); n++ )
{ //Create a point from the polygon
*pInnerSequence++ = awt::Point(basegfx::fround(rPoly.getB2DPoint(n).getX()),
basegfx::fround(rPoly.getB2DPoint(n).getY()));
} return aRetval;
}
//LineStyle : SOLID by default
drawing::LineStyle eLS;
eLS = drawing::LineStyle_SOLID;
aXPropSet->setPropertyValue(u"LineStyle"_ustr, uno::Any(eLS));
//LineCap : ROUND by default, same as in show mode
drawing::LineCap eLC;
eLC = drawing::LineCap_ROUND;
aXPropSet->setPropertyValue(u"LineCap"_ustr, uno::Any(eLC));
//LineColor
sal_uInt32 nLineColor = 0; if (pCanvasPolyPoly)
nLineColor = pCanvasPolyPoly->getRGBALineColor(); //Transform polygon color from RRGGBBAA to AARRGGBB
aXPropSet->setPropertyValue(u"LineColor"_ustr, uno::Any(RGBAColor2UnoColor(nLineColor)));
void SlideShowImpl::registerUserPaintPolygons( const uno::Reference< lang::XMultiServiceFactory >& xDocFactory )
{ //Retrieve Polygons if user ends presentation by context menu if (mpCurrentSlide)
{ if(findPolygons(mpCurrentSlide->getXDrawPage()) != maPolygons.end())
maPolygons.erase(mpCurrentSlide->getXDrawPage());
// ODF defaults from ctor of SdrLayer are not automatically set on the here // created XLayer. Need to be done explicitly here.
aPropLayer <<= true;
xDrawnInSlideshow->setPropertyValue(u"IsVisible"_ustr, aPropLayer);
xDrawnInSlideshow->setPropertyValue(u"IsPrintable"_ustr, aPropLayer);
aPropLayer <<= false;
xDrawnInSlideshow->setPropertyValue(u"IsLocked"_ustr, aPropLayer);
//Register polygons for each slide // The polygons are simplified using the Ramer-Douglas-Peucker algorithm. // This is the therefore needed tolerance. Ideally the value should be user defined. // For now a suitable value is found experimental.
constexpr double fTolerance(12); for (constauto& rPoly : maPolygons)
{
PolyPolygonVector aPolygons = rPoly.second; if (aPolygons.empty()) continue; //Get shapes for the slide
css::uno::Reference<css::drawing::XShapes> Shapes = rPoly.first;
//Retrieve polygons for one slide // #tdf112687 A pen drawing in slideshow is actually a chain of individual line shapes, where // the end point of one line shape equals the start point of the next line shape. // We collect these points into one B2DPolygon and use that to generate the shape on the // slide.
::basegfx::B2DPolygon aDrawingPoints;
cppcanvas::PolyPolygonSharedPtr pFirstPolyPoly = aPolygons.front(); // for style properties for (constauto& pPolyPoly : aPolygons)
{ if (pPolyPoly->getIsFromPreviousSlideshow()) continue; // Actually, each item in aPolygons has two points, but wrapped in a cppcanvas::PopyPolygon.
::basegfx::B2DPolyPolygon b2DPolyPoly
= ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(
pPolyPoly->getUNOPolyPolygon());
//Normally there is only one polygon for (sal_uInt32 i = 0; i < b2DPolyPoly.count(); i++)
{ const ::basegfx::B2DPolygon& aPoly = b2DPolyPoly.getB2DPolygon(i);
if (aPoly.count() > 1) // otherwise skip it, count should be 2
{ constauto ptCount = aDrawingPoints.count(); if (ptCount == 0)
{
aDrawingPoints.append(aPoly);
pFirstPolyPoly = pPolyPoly; continue;
}
basegfx::B2DPoint aLast
= aDrawingPoints.getB2DPoint(ptCount - 1); if (aPoly.getB2DPoint(0).equal(aLast))
{
aDrawingPoints.append(aPoly, 1); continue;
}
// Put what we have collected to the slide and then start a new pen drawing object //create the PolyLineShape. The points will be in its PolyPolygon property.
uno::Reference<uno::XInterface> polyshape(
xDocFactory->createInstance(u"com.sun.star.drawing.PolyLineShape"_ustr));
uno::Reference<drawing::XShape> rPolyShape(polyshape, uno::UNO_QUERY); //Add the shape to the slide
Shapes->add(rPolyShape); //Construct a sequence of points sequence
aDrawingPoints
= basegfx::utils::createSimplifiedPolygon(aDrawingPoints, fTolerance); const drawing::PointSequenceSequence aRetval
= lcl_createPointSequenceSequenceFromB2DPolygon(aDrawingPoints); //Fill the properties
lcl_setPropertiesToShape(aRetval, pFirstPolyPoly, rPolyShape); // make polygons special
xLayerManager->attachShapeToLayer(rPolyShape, xDrawnInSlideshow); // Start next pen drawing object
aDrawingPoints.clear();
aDrawingPoints.append(aPoly);
pFirstPolyPoly = pPolyPoly;
}
}
} // Bring remaining points to slide if (aDrawingPoints.count() > 1)
{ //create the PolyLineShape. The points will be in its PolyPolygon property.
uno::Reference<uno::XInterface> polyshape(
xDocFactory->createInstance(u"com.sun.star.drawing.PolyLineShape"_ustr));
uno::Reference<drawing::XShape> rPolyShape(polyshape, uno::UNO_QUERY); //Add the shape to the slide
Shapes->add(rPolyShape); //Construct a sequence of points sequence
aDrawingPoints = basegfx::utils::createSimplifiedPolygon(aDrawingPoints, fTolerance);
drawing::PointSequenceSequence aRetval
= lcl_createPointSequenceSequenceFromB2DPolygon(aDrawingPoints); //Fill the properties
lcl_setPropertiesToShape(aRetval, aPolygons.back(), rPolyShape); // make polygons special
xLayerManager->attachShapeToLayer(rPolyShape, xDrawnInSlideshow);
}
}
}
// precondition: must only be called from the main thread!
DBG_TESTSOLARMUTEX();
// tdf#160669 IASS: if hint is about PrefetchSlide, flush it to avoid errors if ( rProperty.Name == "HintSlideChanged" )
{
uno::Reference< drawing::XDrawPage > xDrawPage; if (rProperty.Value >>= xDrawPage)
{ if (xDrawPage == mxPrefetchSlide)
{
mxPrefetchSlide.clear();
mpPrefetchSlide.reset();
}
}
}
//adding support for erasing features in UserPaintOverlay if ( rProperty.Name == "EraseAllInk" )
{ bool bEraseAllInk(false); if (rProperty.Value >>= bEraseAllInk)
{
OSL_ENSURE( mbMouseVisible, "setProperty(): User paint overrides invisible mouse" );
if ( rProperty.Name == "IsSoundEnabled" )
{
uno::Sequence<uno::Any> aValues;
uno::Reference<presentation::XSlideShowView> xView; bool bValue (false); if ((rProperty.Value >>= aValues)
&& aValues.getLength()==2
&& (aValues[0] >>= xView)
&& (aValues[1] >>= bValue))
{ // Look up the view. auto iView = std::find_if(maViewContainer.begin(), maViewContainer.end(),
[&xView](const UnoViewSharedPtr& rxView) { return rxView && rxView->getUnoView() == xView; }); if (iView != maViewContainer.end())
{ // Store the flag at the view so that media shapes have // access to it.
(*iView)->setIsSoundEnabled(bValue); returntrue;
}
}
}
// precondition: must only be called from the main thread!
DBG_TESTSOLARMUTEX();
ShapeCursorMap::iterator aIter; if( (aIter=maShapeCursors.find( xShape )) == maShapeCursors.end() )
{ // no entry for this shape -> create one if( nPointerShape != awt::SystemPointer::ARROW )
{ // add new entry, unless shape shall display // normal pointer arrow -> no need to handle that // case
maShapeCursors.emplace(xShape, nPointerShape);
}
} elseif( nPointerShape == awt::SystemPointer::ARROW )
{ // shape shall display normal cursor -> can disable // the cursor and clear the entry
maShapeCursors.erase( xShape );
} else
{ // existing entry found, update with new cursor ID
aIter->second = nPointerShape;
}
}
returnfalse;
} else
{ // TODO(F2): re-evaluate whether that timer lagging makes // sense.
// hold timer, while processing the queues: // 1. when there is more than one active activity this ensures the // same time for all activities and events // 2. processing of events may lead to creation of further events // that have zero delay. While the timer is stopped these events // are processed in the same run.
{ //Get a shared-ptr that outlives the scope-guard which will //ensure that the pointed-to-item exists in the case of a //::dispose clearing mpPresTimer
std::shared_ptr<canvas::tools::ElapsedTime> xTimer(mpPresTimer);
comphelper::ScopeGuard scopeGuard(
[&xTimer]() { return xTimer->releaseTimer(); } );
xTimer->holdTimer();
// process queues
maEventQueue.process();
// #i118671# the call above may execute a macro bound to an object. In // that case this macro may have destroyed this local slideshow so that it // is disposed (see bugdoc at task). In that case, detect this and exit // gently from this slideshow. Do not forget to disable the scoped // call to mpPresTimer, this will be deleted if we are disposed. if (isDisposed())
{
scopeGuard.dismiss(); returnfalse;
}
maActivitiesQueue.process();
// commit frame to screen
maFrameSynchronization.Synchronize();
maScreenUpdater.commitUpdates();
// TODO(Q3): remove need to call dequeued() from // activities. feels like a wart.
// Rationale for ActivitiesQueue::processDequeued(): when // an activity ends, it usually pushed the end state to // the animated shape in question, and ends the animation // (which, in turn, will usually disable shape sprite // mode). Disabling shape sprite mode causes shape // repaint, which, depending on slide content, takes // considerably more time than sprite updates. Thus, the // last animation step tends to look delayed. To // camouflage this, reaching end position and disabling // sprite mode is split into two (normal Activity::end(), // and Activity::dequeued()). Now, the reason to call // commitUpdates() twice here is caused by the unrelated // fact that during wait cursor display/hide, the screen // is updated, and shows hidden sprites, but, in case of // leaving the second commitUpdates() call out and punting // that to the next round, no updated static slide // content. In short, the last shape animation of a slide // tends to blink at its end.
// process dequeued activities _after_ commit to screen
maActivitiesQueue.processDequeued();
// commit frame to screen
maScreenUpdater.commitUpdates();
} // Time held until here
if (bRet)
{ // calc nNextTimeout value: if (bActivitiesLeft)
{ // Activity queue is not empty. Tell caller that we would // like to render another frame.
// Return a zero time-out to signal our caller to call us // back as soon as possible. The actual timing, waiting the // appropriate amount of time between frames, is then done // by the maFrameSynchronization object.
nNextTimeout = 0;
maFrameSynchronization.Activate();
} else
{ // timer events left: // difference from current time (nota bene: // time no longer held here!) to the next event in // the event queue.
// #i61190# Retrieve next timeout only _after_ // processing activity queue
// first init show, to give the animations // the chance to register SlideStartEvents constbool bBackgroundLayerRendered( !bPaintSlide );
mpCurrentSlide->show( bBackgroundLayerRendered );
maEventMultiplexer.notifySlideStartEvent();
}
}
// check whether slide transition should happen // 'automatically'. If yes, simply schedule the // specified timeout. // NOTE: mbForceManualAdvance and mpRehearseTimingsActivity // override any individual slide setting, to always // step slides manually. if( !mbForceManualAdvance &&
!mpRehearseTimingsActivity &&
bHasAutomaticNextSlide &&
mbMovingForward )
{
aNotificationEvents = makeInterruptableDelay(
[this]() { returnthis->notifySlideEnded( false ); },
nAutomaticNextSlideTimeout);
// TODO(F2): Provide a mechanism to let the user override // this automatic timeout via next()
} else
{ if (mpRehearseTimingsActivity)
mpRehearseTimingsActivity->start();
// generate click event. Thus, the user must // trigger the actual end of a slide. No need to // generate interruptable event here, there's no // timeout involved.
aNotificationEvents.mpImmediateEvent =
makeEvent( [this] () { this->notifySlideEnded(false); },
u"SlideShowImpl::notifySlideEnded"_ustr);
}
}
// register events on the queues. To make automatic slide // changes interruptable, register the interruption event // as a nextEffectEvent target. Note that the timeout // event is optional (e.g. manual slide changes don't // generate a timeout)
maUserEventQueue.registerNextEffectEvent(
aNotificationEvents.mpImmediateEvent );
// current slide's main sequence is over. Now should be // the time to prefetch the next slide (if any), and // prepare the initial slide bitmap (speeds up slide // change setup time a lot). Show the wait cursor, this // indeed might take some seconds.
{
WaitSymbolLock aLock (*this);
if (! matches( mpPrefetchSlide,
mxPrefetchSlide, mxPrefetchAnimationNode ))
{
mpPrefetchSlide = makeSlide( mxPrefetchSlide, mxDrawPagesSupplier,
mxPrefetchAnimationNode );
} if (mpPrefetchSlide)
{ // ignore return value, this is just to populate // Slide's internal bitmap buffer, such that the time // needed to generate the slide bitmap is not spent // when the slide change is requested.
mpPrefetchSlide->getCurrentSlideBitmap( *maViewContainer.begin() );
}
} // finally
if (mpRehearseTimingsActivity && !bReverse)
{ constdouble time = mpRehearseTimingsActivity->stop(); if (mpRehearseTimingsActivity->hasBeenClicked())
{ // save time at current drawpage:
uno::Reference<beans::XPropertySet> xPropSet(
mpCurrentSlide->getXDrawPage(), uno::UNO_QUERY );
OSL_ASSERT( xPropSet.is() ); if (xPropSet.is())
{
xPropSet->setPropertyValue(
u"Change"_ustr,
uno::Any( static_cast<sal_Int32>(1) ) );
xPropSet->setPropertyValue(
u"Duration"_ustr,
uno::Any( static_cast<sal_Int32>(time) ) );
}
}
}
if (bReverse)
maEventMultiplexer.notifySlideEndEvent();
stopShow(); // MUST call that: results in // maUserEventQueue.clear(). What's more, // stopShow()'s currSlide->hide() call is // now also required, notifySlideEnded() // relies on that // unconditionally. Otherwise, genuine // shape animations (drawing layer and // GIF) will not be stopped.
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.