Bump version to 6.0-36
[LibreOffice.git] / slideshow / source / engine / rehearsetimingsactivity.cxx
blobdcb6fff399aa5900f8c678ab0c225e47c963e646
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <rtl/ustrbuf.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/gdimtf.hxx>
24 #include <vcl/virdev.hxx>
25 #include <vcl/metric.hxx>
26 #include <vcl/settings.hxx>
28 #include <cppcanvas/vclfactory.hxx>
29 #include <cppcanvas/basegfxfactory.hxx>
30 #include <basegfx/range/b2drange.hxx>
32 #include <comphelper/anytostring.hxx>
33 #include <cppuhelper/exc_hlp.hxx>
35 #include <com/sun/star/awt/MouseButton.hpp>
36 #include <com/sun/star/awt/MouseEvent.hpp>
37 #include <com/sun/star/rendering/XBitmap.hpp>
39 #include <eventqueue.hxx>
40 #include <screenupdater.hxx>
41 #include <eventmultiplexer.hxx>
42 #include <activitiesqueue.hxx>
43 #include <slideshowcontext.hxx>
44 #include <mouseeventhandler.hxx>
45 #include "rehearsetimingsactivity.hxx"
47 #include <algorithm>
49 using namespace com::sun::star;
50 using namespace com::sun::star::uno;
52 namespace slideshow {
53 namespace internal {
55 class RehearseTimingsActivity::WakeupEvent : public Event
57 public:
58 WakeupEvent( std::shared_ptr< ::canvas::tools::ElapsedTime > const& pTimeBase,
59 ActivitySharedPtr const& rActivity,
60 ActivitiesQueue & rActivityQueue ) :
61 Event("WakeupEvent"),
62 maTimer(pTimeBase),
63 mnNextTime(0.0),
64 mpActivity(rActivity),
65 mrActivityQueue( rActivityQueue )
68 WakeupEvent( const WakeupEvent& ) = delete;
69 WakeupEvent& operator=( const WakeupEvent& ) = delete;
71 virtual void dispose() override {}
72 virtual bool fire() override
74 ActivitySharedPtr pActivity( mpActivity.lock() );
75 if( !pActivity )
76 return false;
78 return mrActivityQueue.addActivity( pActivity );
81 virtual bool isCharged() const override { return true; }
82 virtual double getActivationTime( double nCurrentTime ) const override
84 const double nElapsedTime( maTimer.getElapsedTime() );
86 return ::std::max( nCurrentTime,
87 nCurrentTime - nElapsedTime + mnNextTime );
90 /// Start the internal timer
91 void start() { maTimer.reset(); }
93 /** Set the next timeout this object should generate.
95 @param nextTime
96 Absolute time, measured from the last start() call,
97 when this event should wakeup the Activity again. If
98 your time is relative, simply call start() just before
99 every setNextTimeout() call.
101 void setNextTimeout( double nextTime ) { mnNextTime = nextTime; }
103 private:
104 ::canvas::tools::ElapsedTime maTimer;
105 double mnNextTime;
106 std::weak_ptr<Activity> mpActivity;
107 ActivitiesQueue& mrActivityQueue;
110 class RehearseTimingsActivity::MouseHandler : public MouseEventHandler
112 public:
113 explicit MouseHandler( RehearseTimingsActivity& rta );
115 MouseHandler( const MouseHandler& ) = delete;
116 MouseHandler& operator=( const MouseHandler& ) = delete;
118 void reset();
119 bool hasBeenClicked() const { return mbHasBeenClicked; }
121 // MouseEventHandler
122 virtual bool handleMousePressed( awt::MouseEvent const & evt ) override;
123 virtual bool handleMouseReleased( awt::MouseEvent const & evt ) override;
124 virtual bool handleMouseDragged( awt::MouseEvent const & evt ) override;
125 virtual bool handleMouseMoved( awt::MouseEvent const & evt ) override;
127 private:
128 bool isInArea( css::awt::MouseEvent const & evt ) const;
129 void updatePressedState( const bool pressedState ) const;
131 RehearseTimingsActivity& mrActivity;
132 bool mbHasBeenClicked;
133 bool mbMouseStartedInArea;
136 const sal_Int32 LEFT_BORDER_SPACE = 10;
137 const sal_Int32 LOWER_BORDER_SPACE = 30;
139 RehearseTimingsActivity::RehearseTimingsActivity( const SlideShowContext& rContext ) :
140 mrEventQueue(rContext.mrEventQueue),
141 mrScreenUpdater(rContext.mrScreenUpdater),
142 mrEventMultiplexer(rContext.mrEventMultiplexer),
143 mrActivitiesQueue(rContext.mrActivitiesQueue),
144 maElapsedTime( rContext.mrEventQueue.getTimer() ),
145 maViews(),
146 maSpriteRectangle(),
147 maFont( Application::GetSettings().GetStyleSettings().GetLabelFont() ),
148 mpWakeUpEvent(),
149 mpMouseHandler(),
150 maSpriteSizePixel(),
151 mnYOffset(0),
152 mbActive(false),
153 mbDrawPressed(false)
155 maFont.SetFontHeight( maFont.GetFontHeight() * 2 );
156 maFont.SetAverageFontWidth( maFont.GetAverageFontWidth() * 2 );
157 maFont.SetAlignment( ALIGN_BASELINE );
158 maFont.SetColor( COL_BLACK );
160 // determine sprite size (in pixel):
161 ScopedVclPtrInstance< VirtualDevice > blackHole;
162 blackHole->EnableOutput(false);
163 blackHole->SetFont( maFont );
164 blackHole->SetMapMode(MapMode(MapUnit::MapPixel));
165 tools::Rectangle rect;
166 const FontMetric metric( blackHole->GetFontMetric() );
167 blackHole->GetTextBoundRect( rect, "XX:XX:XX" );
168 maSpriteSizePixel.setX( rect.getWidth() * 12 / 10 );
169 maSpriteSizePixel.setY( metric.GetLineHeight() * 11 / 10 );
170 mnYOffset = (metric.GetAscent() + (metric.GetLineHeight() / 20));
172 for( const auto& rView : rContext.mrViewContainer )
173 viewAdded( rView );
176 RehearseTimingsActivity::~RehearseTimingsActivity()
180 stop();
182 catch (const uno::Exception& e)
184 SAL_WARN("slideshow", e);
188 std::shared_ptr<RehearseTimingsActivity> RehearseTimingsActivity::create(
189 const SlideShowContext& rContext )
191 std::shared_ptr<RehearseTimingsActivity> pActivity(
192 new RehearseTimingsActivity( rContext ));
194 pActivity->mpMouseHandler.reset(
195 new MouseHandler(*pActivity.get()) );
196 pActivity->mpWakeUpEvent.reset(
197 new WakeupEvent( rContext.mrEventQueue.getTimer(),
198 pActivity,
199 rContext.mrActivitiesQueue ));
201 rContext.mrEventMultiplexer.addViewHandler( pActivity );
203 return pActivity;
206 void RehearseTimingsActivity::start()
208 maElapsedTime.reset();
209 mbDrawPressed = false;
210 mbActive = true;
212 // paint and show all sprites:
213 paintAllSprites();
214 for_each_sprite( []( const ::cppcanvas::CustomSpriteSharedPtr& pSprite )
215 { return pSprite->show(); } );
217 mrActivitiesQueue.addActivity( std::dynamic_pointer_cast<Activity>(shared_from_this()) );
219 mpMouseHandler->reset();
220 mrEventMultiplexer.addClickHandler(
221 mpMouseHandler, 42 /* highest prio of all, > 3.0 */ );
222 mrEventMultiplexer.addMouseMoveHandler(
223 mpMouseHandler, 42 /* highest prio of all, > 3.0 */ );
226 double RehearseTimingsActivity::stop()
228 mrEventMultiplexer.removeMouseMoveHandler( mpMouseHandler );
229 mrEventMultiplexer.removeClickHandler( mpMouseHandler );
231 mbActive = false; // will be removed from queue
233 for_each_sprite( []( const ::cppcanvas::CustomSpriteSharedPtr& pSprite )
234 { return pSprite->hide(); } );
236 return maElapsedTime.getElapsedTime();
239 bool RehearseTimingsActivity::hasBeenClicked() const
241 if (mpMouseHandler)
242 return mpMouseHandler->hasBeenClicked();
243 return false;
246 // Disposable:
247 void RehearseTimingsActivity::dispose()
249 stop();
251 mpWakeUpEvent.reset();
252 mpMouseHandler.reset();
254 ViewsVecT().swap( maViews );
257 // Activity:
258 double RehearseTimingsActivity::calcTimeLag() const
260 return 0.0;
263 bool RehearseTimingsActivity::perform()
265 if( !isActive() )
266 return false;
268 if( !mpWakeUpEvent )
269 return false;
271 mpWakeUpEvent->start();
272 mpWakeUpEvent->setNextTimeout( 0.5 );
273 mrEventQueue.addEvent( mpWakeUpEvent );
275 paintAllSprites();
277 // sprites changed, need screen update
278 mrScreenUpdater.notifyUpdate();
280 return false; // don't reinsert, WakeupEvent will perform
281 // that after the given timeout
284 bool RehearseTimingsActivity::isActive() const
286 return mbActive;
289 void RehearseTimingsActivity::dequeued()
291 // not used here
294 void RehearseTimingsActivity::end()
296 if (isActive())
298 stop();
299 mbActive = false;
303 basegfx::B2DRange RehearseTimingsActivity::calcSpriteRectangle( UnoViewSharedPtr const& rView ) const
305 const Reference<rendering::XBitmap> xBitmap( rView->getCanvas()->getUNOCanvas(),
306 UNO_QUERY );
307 if( !xBitmap.is() )
308 return basegfx::B2DRange();
310 const geometry::IntegerSize2D realSize( xBitmap->getSize() );
311 // pixel:
312 basegfx::B2DPoint spritePos(
313 std::min<sal_Int32>( realSize.Width, LEFT_BORDER_SPACE ),
314 std::max<sal_Int32>( 0, realSize.Height - maSpriteSizePixel.getY()
315 - LOWER_BORDER_SPACE ) );
316 basegfx::B2DHomMatrix transformation( rView->getTransformation() );
317 transformation.invert();
318 spritePos *= transformation;
319 basegfx::B2DSize spriteSize( maSpriteSizePixel.getX(),
320 maSpriteSizePixel.getY() );
321 spriteSize *= transformation;
322 return basegfx::B2DRange(
323 spritePos.getX(), spritePos.getY(),
324 spritePos.getX() + spriteSize.getX(),
325 spritePos.getY() + spriteSize.getY() );
328 void RehearseTimingsActivity::viewAdded( const UnoViewSharedPtr& rView )
330 cppcanvas::CustomSpriteSharedPtr sprite(
331 rView->createSprite( basegfx::B2DSize(
332 maSpriteSizePixel.getX()+2,
333 maSpriteSizePixel.getY()+2 ),
334 1001.0 )); // sprite should be in front of all
335 // other sprites
336 sprite->setAlpha( 0.8 );
337 const basegfx::B2DRange spriteRectangle(
338 calcSpriteRectangle( rView ) );
339 sprite->move( basegfx::B2DPoint(
340 spriteRectangle.getMinX(),
341 spriteRectangle.getMinY() ) );
343 if( maViews.empty() )
344 maSpriteRectangle = spriteRectangle;
346 maViews.emplace_back( rView, sprite );
348 if (isActive())
349 sprite->show();
352 void RehearseTimingsActivity::viewRemoved( const UnoViewSharedPtr& rView )
354 maViews.erase(
355 std::remove_if( maViews.begin(), maViews.end(),
356 [&rView]
357 ( const ViewsVecT::value_type& cp )
358 { return rView == cp.first; } ),
359 maViews.end() );
362 void RehearseTimingsActivity::viewChanged( const UnoViewSharedPtr& rView )
364 // find entry corresponding to modified view
365 ViewsVecT::iterator aModifiedEntry(
366 std::find_if(
367 maViews.begin(),
368 maViews.end(),
369 [&rView]
370 ( const ViewsVecT::value_type& cp )
371 { return rView == cp.first; } )
374 OSL_ASSERT( aModifiedEntry != maViews.end() );
375 if( aModifiedEntry == maViews.end() )
376 return;
378 // new sprite pos, transformation might have changed:
379 maSpriteRectangle = calcSpriteRectangle( rView );
381 // reposition sprite:
382 aModifiedEntry->second->move( maSpriteRectangle.getMinimum() );
384 // sprites changed, need screen update
385 mrScreenUpdater.notifyUpdate( rView, false );
388 void RehearseTimingsActivity::viewsChanged()
390 if( !maViews.empty() )
392 // new sprite pos, transformation might have changed:
393 maSpriteRectangle = calcSpriteRectangle( maViews.front().first );
395 ::basegfx::B2DPoint nMin = maSpriteRectangle.getMinimum();
396 // reposition sprites
397 for_each_sprite( [nMin]( const ::cppcanvas::CustomSpriteSharedPtr& pSprite )
398 { return pSprite->move( nMin ); } );
400 // sprites changed, need screen update
401 mrScreenUpdater.notifyUpdate();
405 void RehearseTimingsActivity::paintAllSprites() const
407 for_each_sprite(
408 [this]( const ::cppcanvas::CustomSpriteSharedPtr& pSprite )
409 { return this->paint( pSprite->getContentCanvas() ); } );
412 void RehearseTimingsActivity::paint( cppcanvas::CanvasSharedPtr const & canvas ) const
414 // build timer string:
415 const sal_Int32 nTimeSecs =
416 static_cast<sal_Int32>(maElapsedTime.getElapsedTime());
417 OUStringBuffer buf;
418 sal_Int32 n = (nTimeSecs / 3600);
419 if (n < 10)
420 buf.append( '0' );
421 buf.append( n );
422 buf.append( ':' );
423 n = ((nTimeSecs % 3600) / 60);
424 if (n < 10)
425 buf.append( '0' );
426 buf.append( n );
427 buf.append( ':' );
428 n = (nTimeSecs % 60);
429 if (n < 10)
430 buf.append( '0' );
431 buf.append( n );
432 const OUString time = buf.makeStringAndClear();
434 // create the MetaFile:
435 GDIMetaFile metaFile;
436 ScopedVclPtrInstance< VirtualDevice > blackHole;
437 metaFile.Record( blackHole );
438 metaFile.SetPrefSize( Size( 1, 1 ) );
439 blackHole->EnableOutput(false);
440 blackHole->SetMapMode(MapMode(MapUnit::MapPixel));
441 blackHole->SetFont( maFont );
442 tools::Rectangle rect = tools::Rectangle( 0,0,
443 maSpriteSizePixel.getX(),
444 maSpriteSizePixel.getY());
445 if (mbDrawPressed)
447 blackHole->SetTextColor( COL_BLACK );
448 blackHole->SetFillColor( COL_LIGHTGRAY );
449 blackHole->SetLineColor( COL_GRAY );
451 else
453 blackHole->SetTextColor( COL_BLACK );
454 blackHole->SetFillColor( COL_WHITE );
455 blackHole->SetLineColor( COL_GRAY );
457 blackHole->DrawRect( rect );
458 blackHole->GetTextBoundRect( rect, time );
459 blackHole->DrawText(
460 Point( (maSpriteSizePixel.getX() - rect.getWidth()) / 2,
461 mnYOffset ), time );
463 metaFile.Stop();
464 metaFile.WindStart();
466 cppcanvas::RendererSharedPtr renderer(
467 cppcanvas::VCLFactory::createRenderer(
468 canvas, metaFile, cppcanvas::Renderer::Parameters() ) );
469 const bool succ = renderer->draw();
470 OSL_ASSERT( succ );
474 RehearseTimingsActivity::MouseHandler::MouseHandler( RehearseTimingsActivity& rta ) :
475 mrActivity(rta),
476 mbHasBeenClicked(false),
477 mbMouseStartedInArea(false)
480 void RehearseTimingsActivity::MouseHandler::reset()
482 mbHasBeenClicked = false;
483 mbMouseStartedInArea = false;
486 bool RehearseTimingsActivity::MouseHandler::isInArea(
487 awt::MouseEvent const & evt ) const
489 return mrActivity.maSpriteRectangle.isInside(
490 basegfx::B2DPoint( evt.X, evt.Y ) );
493 void RehearseTimingsActivity::MouseHandler::updatePressedState(
494 const bool pressedState ) const
496 if( pressedState != mrActivity.mbDrawPressed )
498 mrActivity.mbDrawPressed = pressedState;
499 mrActivity.paintAllSprites();
501 mrActivity.mrScreenUpdater.notifyUpdate();
505 // MouseEventHandler
506 bool RehearseTimingsActivity::MouseHandler::handleMousePressed(
507 awt::MouseEvent const & evt )
509 if( evt.Buttons == awt::MouseButton::LEFT && isInArea(evt) )
511 mbMouseStartedInArea = true;
512 updatePressedState(true);
513 return true; // consume event
515 return false;
518 bool RehearseTimingsActivity::MouseHandler::handleMouseReleased(
519 awt::MouseEvent const & evt )
521 if( evt.Buttons == awt::MouseButton::LEFT && mbMouseStartedInArea )
523 mbHasBeenClicked = isInArea(evt); // fini if in
524 mbMouseStartedInArea = false;
525 updatePressedState(false);
526 if( !mbHasBeenClicked )
527 return true; // consume event, else next slide (manual advance)
529 return false;
532 bool RehearseTimingsActivity::MouseHandler::handleMouseDragged(
533 awt::MouseEvent const & evt )
535 if( mbMouseStartedInArea )
536 updatePressedState( isInArea(evt) );
537 return false;
540 bool RehearseTimingsActivity::MouseHandler::handleMouseMoved(
541 awt::MouseEvent const & /*evt*/ )
543 return false;
546 } // namespace internal
547 } // namespace presentation
549 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */