build fix
[LibreOffice.git] / slideshow / source / engine / rehearsetimingsactivity.cxx
bloba08b50dd8f42ab1d8b863f88f3afbcd89ef627ba
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( MapUnit::MapPixel );
165 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 (uno::Exception &)
184 OSL_FAIL( OUStringToOString(
185 comphelper::anyToString(
186 cppu::getCaughtException() ),
187 RTL_TEXTENCODING_UTF8 ).getStr() );
191 std::shared_ptr<RehearseTimingsActivity> RehearseTimingsActivity::create(
192 const SlideShowContext& rContext )
194 std::shared_ptr<RehearseTimingsActivity> pActivity(
195 new RehearseTimingsActivity( rContext ));
197 pActivity->mpMouseHandler.reset(
198 new MouseHandler(*pActivity.get()) );
199 pActivity->mpWakeUpEvent.reset(
200 new WakeupEvent( rContext.mrEventQueue.getTimer(),
201 pActivity,
202 rContext.mrActivitiesQueue ));
204 rContext.mrEventMultiplexer.addViewHandler( pActivity );
206 return pActivity;
209 void RehearseTimingsActivity::start()
211 maElapsedTime.reset();
212 mbDrawPressed = false;
213 mbActive = true;
215 // paint and show all sprites:
216 paintAllSprites();
217 for_each_sprite( []( const ::cppcanvas::CustomSpriteSharedPtr& pSprite )
218 { return pSprite->show(); } );
220 mrActivitiesQueue.addActivity( std::dynamic_pointer_cast<Activity>(shared_from_this()) );
222 mpMouseHandler->reset();
223 mrEventMultiplexer.addClickHandler(
224 mpMouseHandler, 42 /* highest prio of all, > 3.0 */ );
225 mrEventMultiplexer.addMouseMoveHandler(
226 mpMouseHandler, 42 /* highest prio of all, > 3.0 */ );
229 double RehearseTimingsActivity::stop()
231 mrEventMultiplexer.removeMouseMoveHandler( mpMouseHandler );
232 mrEventMultiplexer.removeClickHandler( mpMouseHandler );
234 mbActive = false; // will be removed from queue
236 for_each_sprite( []( const ::cppcanvas::CustomSpriteSharedPtr& pSprite )
237 { return pSprite->hide(); } );
239 return maElapsedTime.getElapsedTime();
242 bool RehearseTimingsActivity::hasBeenClicked() const
244 if (mpMouseHandler)
245 return mpMouseHandler->hasBeenClicked();
246 return false;
249 // Disposable:
250 void RehearseTimingsActivity::dispose()
252 stop();
254 mpWakeUpEvent.reset();
255 mpMouseHandler.reset();
257 ViewsVecT().swap( maViews );
260 // Activity:
261 double RehearseTimingsActivity::calcTimeLag() const
263 return 0.0;
266 bool RehearseTimingsActivity::perform()
268 if( !isActive() )
269 return false;
271 if( !mpWakeUpEvent )
272 return false;
274 mpWakeUpEvent->start();
275 mpWakeUpEvent->setNextTimeout( 0.5 );
276 mrEventQueue.addEvent( mpWakeUpEvent );
278 paintAllSprites();
280 // sprites changed, need screen update
281 mrScreenUpdater.notifyUpdate();
283 return false; // don't reinsert, WakeupEvent will perform
284 // that after the given timeout
287 bool RehearseTimingsActivity::isActive() const
289 return mbActive;
292 void RehearseTimingsActivity::dequeued()
294 // not used here
297 void RehearseTimingsActivity::end()
299 if (isActive())
301 stop();
302 mbActive = false;
306 basegfx::B2DRange RehearseTimingsActivity::calcSpriteRectangle( UnoViewSharedPtr const& rView ) const
308 const Reference<rendering::XBitmap> xBitmap( rView->getCanvas()->getUNOCanvas(),
309 UNO_QUERY );
310 if( !xBitmap.is() )
311 return basegfx::B2DRange();
313 const geometry::IntegerSize2D realSize( xBitmap->getSize() );
314 // pixel:
315 basegfx::B2DPoint spritePos(
316 std::min<sal_Int32>( realSize.Width, LEFT_BORDER_SPACE ),
317 std::max<sal_Int32>( 0, realSize.Height - maSpriteSizePixel.getY()
318 - LOWER_BORDER_SPACE ) );
319 basegfx::B2DHomMatrix transformation( rView->getTransformation() );
320 transformation.invert();
321 spritePos *= transformation;
322 basegfx::B2DSize spriteSize( maSpriteSizePixel.getX(),
323 maSpriteSizePixel.getY() );
324 spriteSize *= transformation;
325 return basegfx::B2DRange(
326 spritePos.getX(), spritePos.getY(),
327 spritePos.getX() + spriteSize.getX(),
328 spritePos.getY() + spriteSize.getY() );
331 void RehearseTimingsActivity::viewAdded( const UnoViewSharedPtr& rView )
333 cppcanvas::CustomSpriteSharedPtr sprite(
334 rView->createSprite( basegfx::B2DSize(
335 maSpriteSizePixel.getX()+2,
336 maSpriteSizePixel.getY()+2 ),
337 1001.0 )); // sprite should be in front of all
338 // other sprites
339 sprite->setAlpha( 0.8 );
340 const basegfx::B2DRange spriteRectangle(
341 calcSpriteRectangle( rView ) );
342 sprite->move( basegfx::B2DPoint(
343 spriteRectangle.getMinX(),
344 spriteRectangle.getMinY() ) );
346 if( maViews.empty() )
347 maSpriteRectangle = spriteRectangle;
349 maViews.push_back( ViewsVecT::value_type( rView, sprite ) );
351 if (isActive())
352 sprite->show();
355 void RehearseTimingsActivity::viewRemoved( const UnoViewSharedPtr& rView )
357 maViews.erase(
358 std::remove_if( maViews.begin(), maViews.end(),
359 [&rView]
360 ( const ViewsVecT::value_type& cp )
361 { return rView == cp.first; } ),
362 maViews.end() );
365 void RehearseTimingsActivity::viewChanged( const UnoViewSharedPtr& rView )
367 // find entry corresponding to modified view
368 ViewsVecT::iterator aModifiedEntry(
369 std::find_if(
370 maViews.begin(),
371 maViews.end(),
372 [&rView]
373 ( const ViewsVecT::value_type& cp )
374 { return rView == cp.first; } )
377 OSL_ASSERT( aModifiedEntry != maViews.end() );
378 if( aModifiedEntry == maViews.end() )
379 return;
381 // new sprite pos, transformation might have changed:
382 maSpriteRectangle = calcSpriteRectangle( rView );
384 // reposition sprite:
385 aModifiedEntry->second->move( maSpriteRectangle.getMinimum() );
387 // sprites changed, need screen update
388 mrScreenUpdater.notifyUpdate( rView, false );
391 void RehearseTimingsActivity::viewsChanged()
393 if( !maViews.empty() )
395 // new sprite pos, transformation might have changed:
396 maSpriteRectangle = calcSpriteRectangle( maViews.front().first );
398 ::basegfx::B2DPoint nMin = maSpriteRectangle.getMinimum();
399 // reposition sprites
400 for_each_sprite( [nMin]( const ::cppcanvas::CustomSpriteSharedPtr& pSprite )
401 { return pSprite->move( nMin ); } );
403 // sprites changed, need screen update
404 mrScreenUpdater.notifyUpdate();
408 void RehearseTimingsActivity::paintAllSprites() const
410 for_each_sprite(
411 [this]( const ::cppcanvas::CustomSpriteSharedPtr& pSprite )
412 { return this->paint( pSprite->getContentCanvas() ); } );
415 void RehearseTimingsActivity::paint( cppcanvas::CanvasSharedPtr const & canvas ) const
417 // build timer string:
418 const sal_Int32 nTimeSecs =
419 static_cast<sal_Int32>(maElapsedTime.getElapsedTime());
420 OUStringBuffer buf;
421 sal_Int32 n = (nTimeSecs / 3600);
422 if (n < 10)
423 buf.append( '0' );
424 buf.append( n );
425 buf.append( ':' );
426 n = ((nTimeSecs % 3600) / 60);
427 if (n < 10)
428 buf.append( '0' );
429 buf.append( n );
430 buf.append( ':' );
431 n = (nTimeSecs % 60);
432 if (n < 10)
433 buf.append( '0' );
434 buf.append( n );
435 const OUString time = buf.makeStringAndClear();
437 // create the MetaFile:
438 GDIMetaFile metaFile;
439 ScopedVclPtrInstance< VirtualDevice > blackHole;
440 metaFile.Record( blackHole );
441 metaFile.SetPrefSize( Size( 1, 1 ) );
442 blackHole->EnableOutput(false);
443 blackHole->SetMapMode( MapUnit::MapPixel );
444 blackHole->SetFont( maFont );
445 Rectangle rect = Rectangle( 0,0,
446 maSpriteSizePixel.getX(),
447 maSpriteSizePixel.getY());
448 if (mbDrawPressed)
450 blackHole->SetTextColor( COL_BLACK );
451 blackHole->SetFillColor( COL_LIGHTGRAY );
452 blackHole->SetLineColor( COL_GRAY );
454 else
456 blackHole->SetTextColor( COL_BLACK );
457 blackHole->SetFillColor( COL_WHITE );
458 blackHole->SetLineColor( COL_GRAY );
460 blackHole->DrawRect( rect );
461 blackHole->GetTextBoundRect( rect, time );
462 blackHole->DrawText(
463 Point( (maSpriteSizePixel.getX() - rect.getWidth()) / 2,
464 mnYOffset ), time );
466 metaFile.Stop();
467 metaFile.WindStart();
469 cppcanvas::RendererSharedPtr renderer(
470 cppcanvas::VCLFactory::createRenderer(
471 canvas, metaFile, cppcanvas::Renderer::Parameters() ) );
472 const bool succ = renderer->draw();
473 OSL_ASSERT( succ );
474 (void)succ;
478 RehearseTimingsActivity::MouseHandler::MouseHandler( RehearseTimingsActivity& rta ) :
479 mrActivity(rta),
480 mbHasBeenClicked(false),
481 mbMouseStartedInArea(false)
484 void RehearseTimingsActivity::MouseHandler::reset()
486 mbHasBeenClicked = false;
487 mbMouseStartedInArea = false;
490 bool RehearseTimingsActivity::MouseHandler::isInArea(
491 awt::MouseEvent const & evt ) const
493 return mrActivity.maSpriteRectangle.isInside(
494 basegfx::B2DPoint( evt.X, evt.Y ) );
497 void RehearseTimingsActivity::MouseHandler::updatePressedState(
498 const bool pressedState ) const
500 if( pressedState != mrActivity.mbDrawPressed )
502 mrActivity.mbDrawPressed = pressedState;
503 mrActivity.paintAllSprites();
505 mrActivity.mrScreenUpdater.notifyUpdate();
509 // MouseEventHandler
510 bool RehearseTimingsActivity::MouseHandler::handleMousePressed(
511 awt::MouseEvent const & evt )
513 if( evt.Buttons == awt::MouseButton::LEFT && isInArea(evt) )
515 mbMouseStartedInArea = true;
516 updatePressedState(true);
517 return true; // consume event
519 return false;
522 bool RehearseTimingsActivity::MouseHandler::handleMouseReleased(
523 awt::MouseEvent const & evt )
525 if( evt.Buttons == awt::MouseButton::LEFT && mbMouseStartedInArea )
527 mbHasBeenClicked = isInArea(evt); // fini if in
528 mbMouseStartedInArea = false;
529 updatePressedState(false);
530 if( !mbHasBeenClicked )
531 return true; // consume event, else next slide (manual advance)
533 return false;
536 bool RehearseTimingsActivity::MouseHandler::handleMouseDragged(
537 awt::MouseEvent const & evt )
539 if( mbMouseStartedInArea )
540 updatePressedState( isInArea(evt) );
541 return false;
544 bool RehearseTimingsActivity::MouseHandler::handleMouseMoved(
545 awt::MouseEvent const & /*evt*/ )
547 return false;
550 } // namespace internal
551 } // namespace presentation
553 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */