android: Update app-specific/MIME type icons
[LibreOffice.git] / slideshow / source / engine / rehearsetimingsactivity.cxx
blob8284a998900f72659ecf620c7fd23c5004848180
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 <comphelper/diagnose_ex.hxx>
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.getOpenWidth() * 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.getWidth(),
323 spritePos.getY() + spriteSize.getHeight() );
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( OUString::number(n) + ":" );
420 n = ((nTimeSecs % 3600) / 60);
421 if (n < 10)
422 buf.append( '0' );
423 buf.append( OUString::number(n) + ":" );
424 n = (nTimeSecs % 60);
425 if (n < 10)
426 buf.append( '0' );
427 buf.append( n );
428 const OUString time = buf.makeStringAndClear();
430 // create the MetaFile:
431 GDIMetaFile metaFile;
432 ScopedVclPtrInstance< VirtualDevice > blackHole;
433 metaFile.Record( blackHole );
434 metaFile.SetPrefSize( Size( 1, 1 ) );
435 blackHole->EnableOutput(false);
436 blackHole->SetMapMode(MapMode(MapUnit::MapPixel));
437 blackHole->SetFont( maFont );
438 tools::Rectangle rect( 0,0,
439 maSpriteSizePixel.getX(),
440 maSpriteSizePixel.getY());
441 if (mbDrawPressed)
443 blackHole->SetTextColor( COL_BLACK );
444 blackHole->SetFillColor( COL_LIGHTGRAY );
445 blackHole->SetLineColor( COL_GRAY );
447 else
449 blackHole->SetTextColor( COL_BLACK );
450 blackHole->SetFillColor( COL_WHITE );
451 blackHole->SetLineColor( COL_GRAY );
453 blackHole->DrawRect( rect );
454 blackHole->GetTextBoundRect( rect, time );
455 blackHole->DrawText(
456 Point( (maSpriteSizePixel.getX() - rect.getOpenWidth()) / 2,
457 mnYOffset ), time );
459 metaFile.Stop();
460 metaFile.WindStart();
462 cppcanvas::RendererSharedPtr renderer(
463 cppcanvas::VCLFactory::createRenderer(
464 canvas, metaFile, cppcanvas::Renderer::Parameters() ) );
465 const bool succ = renderer->draw();
466 OSL_ASSERT( succ );
470 RehearseTimingsActivity::MouseHandler::MouseHandler( RehearseTimingsActivity& rta ) :
471 mrActivity(rta),
472 mbHasBeenClicked(false),
473 mbMouseStartedInArea(false)
476 void RehearseTimingsActivity::MouseHandler::reset()
478 mbHasBeenClicked = false;
479 mbMouseStartedInArea = false;
482 bool RehearseTimingsActivity::MouseHandler::isInArea(
483 awt::MouseEvent const & evt ) const
485 return mrActivity.maSpriteRectangle.isInside(
486 basegfx::B2DPoint( evt.X, evt.Y ) );
489 void RehearseTimingsActivity::MouseHandler::updatePressedState(
490 const bool pressedState ) const
492 if( pressedState != mrActivity.mbDrawPressed )
494 mrActivity.mbDrawPressed = pressedState;
495 mrActivity.paintAllSprites();
497 mrActivity.mrScreenUpdater.notifyUpdate();
501 // MouseEventHandler
502 bool RehearseTimingsActivity::MouseHandler::handleMousePressed(
503 awt::MouseEvent const & evt )
505 if( evt.Buttons == awt::MouseButton::LEFT && isInArea(evt) )
507 mbMouseStartedInArea = true;
508 updatePressedState(true);
509 return true; // consume event
511 return false;
514 bool RehearseTimingsActivity::MouseHandler::handleMouseReleased(
515 awt::MouseEvent const & evt )
517 if( evt.Buttons == awt::MouseButton::LEFT && mbMouseStartedInArea )
519 mbHasBeenClicked = isInArea(evt); // fini if in
520 mbMouseStartedInArea = false;
521 updatePressedState(false);
522 if( !mbHasBeenClicked )
523 return true; // consume event, else next slide (manual advance)
525 return false;
528 bool RehearseTimingsActivity::MouseHandler::handleMouseDragged(
529 awt::MouseEvent const & evt )
531 if( mbMouseStartedInArea )
532 updatePressedState( isInArea(evt) );
533 return false;
536 bool RehearseTimingsActivity::MouseHandler::handleMouseMoved(
537 awt::MouseEvent const & /*evt*/ )
539 return false;
542 } // namespace presentation
544 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */