Use o3tl::convert in Math
[LibreOffice.git] / slideshow / source / engine / rehearsetimingsactivity.cxx
blobdd4e5beac67fac7da139e7c9944e498c4397eee5
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 <basegfx/range/b2drange.hxx>
30 #include <osl/diagnose.h>
31 #include <tools/diagnose_ex.h>
33 #include <com/sun/star/awt/MouseButton.hpp>
34 #include <com/sun/star/awt/MouseEvent.hpp>
35 #include <com/sun/star/rendering/XBitmap.hpp>
36 #include <com/sun/star/rendering/XCanvas.hpp>
38 #include <eventqueue.hxx>
39 #include <screenupdater.hxx>
40 #include <eventmultiplexer.hxx>
41 #include <activitiesqueue.hxx>
42 #include <slideshowcontext.hxx>
43 #include <mouseeventhandler.hxx>
44 #include "rehearsetimingsactivity.hxx"
46 #include <algorithm>
48 using namespace com::sun::star;
49 using namespace com::sun::star::uno;
51 namespace slideshow::internal {
53 class RehearseTimingsActivity::WakeupEvent : public Event
55 public:
56 WakeupEvent( std::shared_ptr< ::canvas::tools::ElapsedTime > const& pTimeBase,
57 ActivitySharedPtr const& rActivity,
58 ActivitiesQueue & rActivityQueue ) :
59 Event("WakeupEvent"),
60 maTimer(pTimeBase),
61 mnNextTime(0.0),
62 mpActivity(rActivity),
63 mrActivityQueue( rActivityQueue )
66 WakeupEvent( const WakeupEvent& ) = delete;
67 WakeupEvent& operator=( const WakeupEvent& ) = delete;
69 virtual void dispose() override {}
70 virtual bool fire() override
72 ActivitySharedPtr pActivity( mpActivity.lock() );
73 if( !pActivity )
74 return false;
76 return mrActivityQueue.addActivity( pActivity );
79 virtual bool isCharged() const override { return true; }
80 virtual double getActivationTime( double nCurrentTime ) const override
82 const double nElapsedTime( maTimer.getElapsedTime() );
84 return ::std::max( nCurrentTime,
85 nCurrentTime - nElapsedTime + mnNextTime );
88 /// Start the internal timer
89 void start() { maTimer.reset(); }
91 /** Set the next timeout this object should generate.
93 @param nextTime
94 Absolute time, measured from the last start() call,
95 when this event should wakeup the Activity again. If
96 your time is relative, simply call start() just before
97 every setNextTimeout() call.
99 void setNextTimeout( double nextTime ) { mnNextTime = nextTime; }
101 private:
102 ::canvas::tools::ElapsedTime maTimer;
103 double mnNextTime;
104 std::weak_ptr<Activity> mpActivity;
105 ActivitiesQueue& mrActivityQueue;
108 class RehearseTimingsActivity::MouseHandler : public MouseEventHandler
110 public:
111 explicit MouseHandler( RehearseTimingsActivity& rta );
113 MouseHandler( const MouseHandler& ) = delete;
114 MouseHandler& operator=( const MouseHandler& ) = delete;
116 void reset();
117 bool hasBeenClicked() const { return mbHasBeenClicked; }
119 // MouseEventHandler
120 virtual bool handleMousePressed( awt::MouseEvent const & evt ) override;
121 virtual bool handleMouseReleased( awt::MouseEvent const & evt ) override;
122 virtual bool handleMouseDragged( awt::MouseEvent const & evt ) override;
123 virtual bool handleMouseMoved( awt::MouseEvent const & evt ) override;
125 private:
126 bool isInArea( css::awt::MouseEvent const & evt ) const;
127 void updatePressedState( const bool pressedState ) const;
129 RehearseTimingsActivity& mrActivity;
130 bool mbHasBeenClicked;
131 bool mbMouseStartedInArea;
134 const sal_Int32 LEFT_BORDER_SPACE = 10;
135 const sal_Int32 LOWER_BORDER_SPACE = 30;
137 RehearseTimingsActivity::RehearseTimingsActivity( const SlideShowContext& rContext ) :
138 mrEventQueue(rContext.mrEventQueue),
139 mrScreenUpdater(rContext.mrScreenUpdater),
140 mrEventMultiplexer(rContext.mrEventMultiplexer),
141 mrActivitiesQueue(rContext.mrActivitiesQueue),
142 maElapsedTime( rContext.mrEventQueue.getTimer() ),
143 maViews(),
144 maSpriteRectangle(),
145 maFont( Application::GetSettings().GetStyleSettings().GetLabelFont() ),
146 mpWakeUpEvent(),
147 mpMouseHandler(),
148 maSpriteSizePixel(),
149 mnYOffset(0),
150 mbActive(false),
151 mbDrawPressed(false)
153 maFont.SetFontHeight( maFont.GetFontHeight() * 2 );
154 maFont.SetAverageFontWidth( maFont.GetAverageFontWidth() * 2 );
155 maFont.SetAlignment( ALIGN_BASELINE );
156 maFont.SetColor( COL_BLACK );
158 // determine sprite size (in pixel):
159 ScopedVclPtrInstance< VirtualDevice > blackHole;
160 blackHole->EnableOutput(false);
161 blackHole->SetFont( maFont );
162 blackHole->SetMapMode(MapMode(MapUnit::MapPixel));
163 tools::Rectangle rect;
164 const FontMetric metric( blackHole->GetFontMetric() );
165 blackHole->GetTextBoundRect( rect, "XX:XX:XX" );
166 maSpriteSizePixel.setX( rect.getWidth() * 12 / 10 );
167 maSpriteSizePixel.setY( metric.GetLineHeight() * 11 / 10 );
168 mnYOffset = (metric.GetAscent() + (metric.GetLineHeight() / 20));
170 for( const auto& rView : rContext.mrViewContainer )
171 viewAdded( rView );
174 RehearseTimingsActivity::~RehearseTimingsActivity()
178 stop();
180 catch (const uno::Exception&)
182 TOOLS_WARN_EXCEPTION("slideshow", "");
186 std::shared_ptr<RehearseTimingsActivity> RehearseTimingsActivity::create(
187 const SlideShowContext& rContext )
189 std::shared_ptr<RehearseTimingsActivity> pActivity(
190 new RehearseTimingsActivity( rContext ));
192 pActivity->mpMouseHandler =
193 std::make_shared<MouseHandler>(*pActivity);
194 pActivity->mpWakeUpEvent =
195 std::make_shared<WakeupEvent>( rContext.mrEventQueue.getTimer(),
196 pActivity,
197 rContext.mrActivitiesQueue );
199 rContext.mrEventMultiplexer.addViewHandler( pActivity );
201 return pActivity;
204 void RehearseTimingsActivity::start()
206 maElapsedTime.reset();
207 mbDrawPressed = false;
208 mbActive = true;
210 // paint and show all sprites:
211 paintAllSprites();
212 for_each_sprite( []( const ::cppcanvas::CustomSpriteSharedPtr& pSprite )
213 { return pSprite->show(); } );
215 mrActivitiesQueue.addActivity( std::dynamic_pointer_cast<Activity>(shared_from_this()) );
217 mpMouseHandler->reset();
218 mrEventMultiplexer.addClickHandler(
219 mpMouseHandler, 42 /* highest prio of all, > 3.0 */ );
220 mrEventMultiplexer.addMouseMoveHandler(
221 mpMouseHandler, 42 /* highest prio of all, > 3.0 */ );
224 double RehearseTimingsActivity::stop()
226 mrEventMultiplexer.removeMouseMoveHandler( mpMouseHandler );
227 mrEventMultiplexer.removeClickHandler( mpMouseHandler );
229 mbActive = false; // will be removed from queue
231 for_each_sprite( []( const ::cppcanvas::CustomSpriteSharedPtr& pSprite )
232 { return pSprite->hide(); } );
234 return maElapsedTime.getElapsedTime();
237 bool RehearseTimingsActivity::hasBeenClicked() const
239 if (mpMouseHandler)
240 return mpMouseHandler->hasBeenClicked();
241 return false;
244 // Disposable:
245 void RehearseTimingsActivity::dispose()
247 stop();
249 mpWakeUpEvent.reset();
250 mpMouseHandler.reset();
252 ViewsVecT().swap( maViews );
255 // Activity:
256 double RehearseTimingsActivity::calcTimeLag() const
258 return 0.0;
261 bool RehearseTimingsActivity::perform()
263 if( !isActive() )
264 return false;
266 if( !mpWakeUpEvent )
267 return false;
269 mpWakeUpEvent->start();
270 mpWakeUpEvent->setNextTimeout( 0.5 );
271 mrEventQueue.addEvent( mpWakeUpEvent );
273 paintAllSprites();
275 // sprites changed, need screen update
276 mrScreenUpdater.notifyUpdate();
278 return false; // don't reinsert, WakeupEvent will perform
279 // that after the given timeout
282 bool RehearseTimingsActivity::isActive() const
284 return mbActive;
287 void RehearseTimingsActivity::dequeued()
289 // not used here
292 void RehearseTimingsActivity::end()
294 if (isActive())
296 stop();
297 mbActive = false;
301 basegfx::B2DRange RehearseTimingsActivity::calcSpriteRectangle( UnoViewSharedPtr const& rView ) const
303 const Reference<rendering::XBitmap> xBitmap( rView->getCanvas()->getUNOCanvas(),
304 UNO_QUERY );
305 if( !xBitmap.is() )
306 return basegfx::B2DRange();
308 const geometry::IntegerSize2D realSize( xBitmap->getSize() );
309 // pixel:
310 basegfx::B2DPoint spritePos(
311 std::min<sal_Int32>( realSize.Width, LEFT_BORDER_SPACE ),
312 std::max<sal_Int32>( 0, realSize.Height - maSpriteSizePixel.getY()
313 - LOWER_BORDER_SPACE ) );
314 basegfx::B2DHomMatrix transformation( rView->getTransformation() );
315 transformation.invert();
316 spritePos *= transformation;
317 basegfx::B2DSize spriteSize( maSpriteSizePixel.getX(),
318 maSpriteSizePixel.getY() );
319 spriteSize *= transformation;
320 return basegfx::B2DRange(
321 spritePos.getX(), spritePos.getY(),
322 spritePos.getX() + spriteSize.getX(),
323 spritePos.getY() + spriteSize.getY() );
326 void RehearseTimingsActivity::viewAdded( const UnoViewSharedPtr& rView )
328 cppcanvas::CustomSpriteSharedPtr sprite(
329 rView->createSprite( basegfx::B2DSize(
330 maSpriteSizePixel.getX()+2,
331 maSpriteSizePixel.getY()+2 ),
332 1001.0 )); // sprite should be in front of all
333 // other sprites
334 sprite->setAlpha( 0.8 );
335 const basegfx::B2DRange spriteRectangle(
336 calcSpriteRectangle( rView ) );
337 sprite->move( basegfx::B2DPoint(
338 spriteRectangle.getMinX(),
339 spriteRectangle.getMinY() ) );
341 if( maViews.empty() )
342 maSpriteRectangle = spriteRectangle;
344 maViews.emplace_back( rView, sprite );
346 if (isActive())
347 sprite->show();
350 void RehearseTimingsActivity::viewRemoved( const UnoViewSharedPtr& rView )
352 maViews.erase(
353 std::remove_if( maViews.begin(), maViews.end(),
354 [&rView]
355 ( const ViewsVecT::value_type& cp )
356 { return rView == cp.first; } ),
357 maViews.end() );
360 void RehearseTimingsActivity::viewChanged( const UnoViewSharedPtr& rView )
362 // find entry corresponding to modified view
363 ViewsVecT::iterator aModifiedEntry(
364 std::find_if(
365 maViews.begin(),
366 maViews.end(),
367 [&rView]
368 ( const ViewsVecT::value_type& cp )
369 { return rView == cp.first; } )
372 OSL_ASSERT( aModifiedEntry != maViews.end() );
373 if( aModifiedEntry == maViews.end() )
374 return;
376 // new sprite pos, transformation might have changed:
377 maSpriteRectangle = calcSpriteRectangle( rView );
379 // reposition sprite:
380 aModifiedEntry->second->move( maSpriteRectangle.getMinimum() );
382 // sprites changed, need screen update
383 mrScreenUpdater.notifyUpdate( rView, false );
386 void RehearseTimingsActivity::viewsChanged()
388 if( maViews.empty() )
389 return;
391 // new sprite pos, transformation might have changed:
392 maSpriteRectangle = calcSpriteRectangle( maViews.front().first );
394 ::basegfx::B2DPoint nMin = maSpriteRectangle.getMinimum();
395 // reposition sprites
396 for_each_sprite( [nMin]( const ::cppcanvas::CustomSpriteSharedPtr& pSprite )
397 { return pSprite->move( nMin ); } );
399 // sprites changed, need screen update
400 mrScreenUpdater.notifyUpdate();
403 void RehearseTimingsActivity::paintAllSprites() const
405 for_each_sprite(
406 [this]( const ::cppcanvas::CustomSpriteSharedPtr& pSprite )
407 { return this->paint( pSprite->getContentCanvas() ); } );
410 void RehearseTimingsActivity::paint( cppcanvas::CanvasSharedPtr const & canvas ) const
412 // build timer string:
413 const sal_Int32 nTimeSecs =
414 static_cast<sal_Int32>(maElapsedTime.getElapsedTime());
415 OUStringBuffer buf;
416 sal_Int32 n = nTimeSecs / 3600;
417 if (n < 10)
418 buf.append( '0' );
419 buf.append( n );
420 buf.append( ':' );
421 n = ((nTimeSecs % 3600) / 60);
422 if (n < 10)
423 buf.append( '0' );
424 buf.append( n );
425 buf.append( ':' );
426 n = (nTimeSecs % 60);
427 if (n < 10)
428 buf.append( '0' );
429 buf.append( n );
430 const OUString time = buf.makeStringAndClear();
432 // create the MetaFile:
433 GDIMetaFile metaFile;
434 ScopedVclPtrInstance< VirtualDevice > blackHole;
435 metaFile.Record( blackHole );
436 metaFile.SetPrefSize( Size( 1, 1 ) );
437 blackHole->EnableOutput(false);
438 blackHole->SetMapMode(MapMode(MapUnit::MapPixel));
439 blackHole->SetFont( maFont );
440 tools::Rectangle rect( 0,0,
441 maSpriteSizePixel.getX(),
442 maSpriteSizePixel.getY());
443 if (mbDrawPressed)
445 blackHole->SetTextColor( COL_BLACK );
446 blackHole->SetFillColor( COL_LIGHTGRAY );
447 blackHole->SetLineColor( COL_GRAY );
449 else
451 blackHole->SetTextColor( COL_BLACK );
452 blackHole->SetFillColor( COL_WHITE );
453 blackHole->SetLineColor( COL_GRAY );
455 blackHole->DrawRect( rect );
456 blackHole->GetTextBoundRect( rect, time );
457 blackHole->DrawText(
458 Point( (maSpriteSizePixel.getX() - rect.getWidth()) / 2,
459 mnYOffset ), time );
461 metaFile.Stop();
462 metaFile.WindStart();
464 cppcanvas::RendererSharedPtr renderer(
465 cppcanvas::VCLFactory::createRenderer(
466 canvas, metaFile, cppcanvas::Renderer::Parameters() ) );
467 const bool succ = renderer->draw();
468 OSL_ASSERT( succ );
472 RehearseTimingsActivity::MouseHandler::MouseHandler( RehearseTimingsActivity& rta ) :
473 mrActivity(rta),
474 mbHasBeenClicked(false),
475 mbMouseStartedInArea(false)
478 void RehearseTimingsActivity::MouseHandler::reset()
480 mbHasBeenClicked = false;
481 mbMouseStartedInArea = false;
484 bool RehearseTimingsActivity::MouseHandler::isInArea(
485 awt::MouseEvent const & evt ) const
487 return mrActivity.maSpriteRectangle.isInside(
488 basegfx::B2DPoint( evt.X, evt.Y ) );
491 void RehearseTimingsActivity::MouseHandler::updatePressedState(
492 const bool pressedState ) const
494 if( pressedState != mrActivity.mbDrawPressed )
496 mrActivity.mbDrawPressed = pressedState;
497 mrActivity.paintAllSprites();
499 mrActivity.mrScreenUpdater.notifyUpdate();
503 // MouseEventHandler
504 bool RehearseTimingsActivity::MouseHandler::handleMousePressed(
505 awt::MouseEvent const & evt )
507 if( evt.Buttons == awt::MouseButton::LEFT && isInArea(evt) )
509 mbMouseStartedInArea = true;
510 updatePressedState(true);
511 return true; // consume event
513 return false;
516 bool RehearseTimingsActivity::MouseHandler::handleMouseReleased(
517 awt::MouseEvent const & evt )
519 if( evt.Buttons == awt::MouseButton::LEFT && mbMouseStartedInArea )
521 mbHasBeenClicked = isInArea(evt); // fini if in
522 mbMouseStartedInArea = false;
523 updatePressedState(false);
524 if( !mbHasBeenClicked )
525 return true; // consume event, else next slide (manual advance)
527 return false;
530 bool RehearseTimingsActivity::MouseHandler::handleMouseDragged(
531 awt::MouseEvent const & evt )
533 if( mbMouseStartedInArea )
534 updatePressedState( isInArea(evt) );
535 return false;
538 bool RehearseTimingsActivity::MouseHandler::handleMouseMoved(
539 awt::MouseEvent const & /*evt*/ )
541 return false;
544 } // namespace presentation
546 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */