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(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
)
176 RehearseTimingsActivity::~RehearseTimingsActivity()
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(),
199 rContext
.mrActivitiesQueue
));
201 rContext
.mrEventMultiplexer
.addViewHandler( pActivity
);
206 void RehearseTimingsActivity::start()
208 maElapsedTime
.reset();
209 mbDrawPressed
= false;
212 // paint and show all sprites:
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
242 return mpMouseHandler
->hasBeenClicked();
247 void RehearseTimingsActivity::dispose()
251 mpWakeUpEvent
.reset();
252 mpMouseHandler
.reset();
254 ViewsVecT().swap( maViews
);
258 double RehearseTimingsActivity::calcTimeLag() const
263 bool RehearseTimingsActivity::perform()
271 mpWakeUpEvent
->start();
272 mpWakeUpEvent
->setNextTimeout( 0.5 );
273 mrEventQueue
.addEvent( mpWakeUpEvent
);
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
289 void RehearseTimingsActivity::dequeued()
294 void RehearseTimingsActivity::end()
303 basegfx::B2DRange
RehearseTimingsActivity::calcSpriteRectangle( UnoViewSharedPtr
const& rView
) const
305 const Reference
<rendering::XBitmap
> xBitmap( rView
->getCanvas()->getUNOCanvas(),
308 return basegfx::B2DRange();
310 const geometry::IntegerSize2D
realSize( xBitmap
->getSize() );
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
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
);
352 void RehearseTimingsActivity::viewRemoved( const UnoViewSharedPtr
& rView
)
355 std::remove_if( maViews
.begin(), maViews
.end(),
357 ( const ViewsVecT::value_type
& cp
)
358 { return rView
== cp
.first
; } ),
362 void RehearseTimingsActivity::viewChanged( const UnoViewSharedPtr
& rView
)
364 // find entry corresponding to modified view
365 ViewsVecT::iterator
aModifiedEntry(
370 ( const ViewsVecT::value_type
& cp
)
371 { return rView
== cp
.first
; } )
374 OSL_ASSERT( aModifiedEntry
!= maViews
.end() );
375 if( aModifiedEntry
== maViews
.end() )
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
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());
418 sal_Int32 n
= (nTimeSecs
/ 3600);
423 n
= ((nTimeSecs
% 3600) / 60);
428 n
= (nTimeSecs
% 60);
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());
447 blackHole
->SetTextColor( COL_BLACK
);
448 blackHole
->SetFillColor( COL_LIGHTGRAY
);
449 blackHole
->SetLineColor( COL_GRAY
);
453 blackHole
->SetTextColor( COL_BLACK
);
454 blackHole
->SetFillColor( COL_WHITE
);
455 blackHole
->SetLineColor( COL_GRAY
);
457 blackHole
->DrawRect( rect
);
458 blackHole
->GetTextBoundRect( rect
, time
);
460 Point( (maSpriteSizePixel
.getX() - rect
.getWidth()) / 2,
464 metaFile
.WindStart();
466 cppcanvas::RendererSharedPtr
renderer(
467 cppcanvas::VCLFactory::createRenderer(
468 canvas
, metaFile
, cppcanvas::Renderer::Parameters() ) );
469 const bool succ
= renderer
->draw();
474 RehearseTimingsActivity::MouseHandler::MouseHandler( RehearseTimingsActivity
& 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();
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
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)
532 bool RehearseTimingsActivity::MouseHandler::handleMouseDragged(
533 awt::MouseEvent
const & evt
)
535 if( mbMouseStartedInArea
)
536 updatePressedState( isInArea(evt
) );
540 bool RehearseTimingsActivity::MouseHandler::handleMouseMoved(
541 awt::MouseEvent
const & /*evt*/ )
546 } // namespace internal
547 } // namespace presentation
549 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */