1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
49 using namespace com::sun::star
;
50 using namespace com::sun::star::uno
;
55 class RehearseTimingsActivity::WakeupEvent
: public Event
58 WakeupEvent( std::shared_ptr
< ::canvas::tools::ElapsedTime
> const& pTimeBase
,
59 ActivitySharedPtr
const& rActivity
,
60 ActivitiesQueue
& rActivityQueue
) :
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() );
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.
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
; }
104 ::canvas::tools::ElapsedTime maTimer
;
106 std::weak_ptr
<Activity
> mpActivity
;
107 ActivitiesQueue
& mrActivityQueue
;
110 class RehearseTimingsActivity::MouseHandler
: public MouseEventHandler
113 explicit MouseHandler( RehearseTimingsActivity
& rta
);
115 MouseHandler( const MouseHandler
& ) = delete;
116 MouseHandler
& operator=( const MouseHandler
& ) = delete;
119 bool hasBeenClicked() const { return mbHasBeenClicked
; }
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
;
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() ),
147 maFont( Application::GetSettings().GetStyleSettings().GetLabelFont() ),
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
);
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
)
176 RehearseTimingsActivity::~RehearseTimingsActivity()
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(),
202 rContext
.mrActivitiesQueue
));
204 rContext
.mrEventMultiplexer
.addViewHandler( pActivity
);
209 void RehearseTimingsActivity::start()
211 maElapsedTime
.reset();
212 mbDrawPressed
= false;
215 // paint and show all sprites:
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
245 return mpMouseHandler
->hasBeenClicked();
250 void RehearseTimingsActivity::dispose()
254 mpWakeUpEvent
.reset();
255 mpMouseHandler
.reset();
257 ViewsVecT().swap( maViews
);
261 double RehearseTimingsActivity::calcTimeLag() const
266 bool RehearseTimingsActivity::perform()
274 mpWakeUpEvent
->start();
275 mpWakeUpEvent
->setNextTimeout( 0.5 );
276 mrEventQueue
.addEvent( mpWakeUpEvent
);
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
292 void RehearseTimingsActivity::dequeued()
297 void RehearseTimingsActivity::end()
306 basegfx::B2DRange
RehearseTimingsActivity::calcSpriteRectangle( UnoViewSharedPtr
const& rView
) const
308 const Reference
<rendering::XBitmap
> xBitmap( rView
->getCanvas()->getUNOCanvas(),
311 return basegfx::B2DRange();
313 const geometry::IntegerSize2D
realSize( xBitmap
->getSize() );
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
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
) );
355 void RehearseTimingsActivity::viewRemoved( const UnoViewSharedPtr
& rView
)
358 std::remove_if( maViews
.begin(), maViews
.end(),
360 ( const ViewsVecT::value_type
& cp
)
361 { return rView
== cp
.first
; } ),
365 void RehearseTimingsActivity::viewChanged( const UnoViewSharedPtr
& rView
)
367 // find entry corresponding to modified view
368 ViewsVecT::iterator
aModifiedEntry(
373 ( const ViewsVecT::value_type
& cp
)
374 { return rView
== cp
.first
; } )
377 OSL_ASSERT( aModifiedEntry
!= maViews
.end() );
378 if( aModifiedEntry
== maViews
.end() )
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
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());
421 sal_Int32 n
= (nTimeSecs
/ 3600);
426 n
= ((nTimeSecs
% 3600) / 60);
431 n
= (nTimeSecs
% 60);
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());
450 blackHole
->SetTextColor( COL_BLACK
);
451 blackHole
->SetFillColor( COL_LIGHTGRAY
);
452 blackHole
->SetLineColor( COL_GRAY
);
456 blackHole
->SetTextColor( COL_BLACK
);
457 blackHole
->SetFillColor( COL_WHITE
);
458 blackHole
->SetLineColor( COL_GRAY
);
460 blackHole
->DrawRect( rect
);
461 blackHole
->GetTextBoundRect( rect
, time
);
463 Point( (maSpriteSizePixel
.getX() - rect
.getWidth()) / 2,
467 metaFile
.WindStart();
469 cppcanvas::RendererSharedPtr
renderer(
470 cppcanvas::VCLFactory::createRenderer(
471 canvas
, metaFile
, cppcanvas::Renderer::Parameters() ) );
472 const bool succ
= renderer
->draw();
478 RehearseTimingsActivity::MouseHandler::MouseHandler( RehearseTimingsActivity
& 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();
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
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)
536 bool RehearseTimingsActivity::MouseHandler::handleMouseDragged(
537 awt::MouseEvent
const & evt
)
539 if( mbMouseStartedInArea
)
540 updatePressedState( isInArea(evt
) );
544 bool RehearseTimingsActivity::MouseHandler::handleMouseMoved(
545 awt::MouseEvent
const & /*evt*/ )
550 } // namespace internal
551 } // namespace presentation
553 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */