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 <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"
48 using namespace com::sun::star
;
49 using namespace com::sun::star::uno
;
51 namespace slideshow::internal
{
53 class RehearseTimingsActivity::WakeupEvent
: public Event
56 WakeupEvent( std::shared_ptr
< ::canvas::tools::ElapsedTime
> const& pTimeBase
,
57 ActivitySharedPtr
const& rActivity
,
58 ActivitiesQueue
& rActivityQueue
) :
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() );
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.
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
; }
102 ::canvas::tools::ElapsedTime maTimer
;
104 std::weak_ptr
<Activity
> mpActivity
;
105 ActivitiesQueue
& mrActivityQueue
;
108 class RehearseTimingsActivity::MouseHandler
: public MouseEventHandler
111 explicit MouseHandler( RehearseTimingsActivity
& rta
);
113 MouseHandler( const MouseHandler
& ) = delete;
114 MouseHandler
& operator=( const MouseHandler
& ) = delete;
117 bool hasBeenClicked() const { return mbHasBeenClicked
; }
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
;
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() ),
145 maFont( Application::GetSettings().GetStyleSettings().GetLabelFont() ),
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
)
174 RehearseTimingsActivity::~RehearseTimingsActivity()
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(),
197 rContext
.mrActivitiesQueue
);
199 rContext
.mrEventMultiplexer
.addViewHandler( pActivity
);
204 void RehearseTimingsActivity::start()
206 maElapsedTime
.reset();
207 mbDrawPressed
= false;
210 // paint and show all sprites:
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
240 return mpMouseHandler
->hasBeenClicked();
245 void RehearseTimingsActivity::dispose()
249 mpWakeUpEvent
.reset();
250 mpMouseHandler
.reset();
252 ViewsVecT().swap( maViews
);
256 double RehearseTimingsActivity::calcTimeLag() const
261 bool RehearseTimingsActivity::perform()
269 mpWakeUpEvent
->start();
270 mpWakeUpEvent
->setNextTimeout( 0.5 );
271 mrEventQueue
.addEvent( mpWakeUpEvent
);
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
287 void RehearseTimingsActivity::dequeued()
292 void RehearseTimingsActivity::end()
301 basegfx::B2DRange
RehearseTimingsActivity::calcSpriteRectangle( UnoViewSharedPtr
const& rView
) const
303 const Reference
<rendering::XBitmap
> xBitmap( rView
->getCanvas()->getUNOCanvas(),
306 return basegfx::B2DRange();
308 const geometry::IntegerSize2D
realSize( xBitmap
->getSize() );
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
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
);
350 void RehearseTimingsActivity::viewRemoved( const UnoViewSharedPtr
& rView
)
353 std::remove_if( maViews
.begin(), maViews
.end(),
355 ( const ViewsVecT::value_type
& cp
)
356 { return rView
== cp
.first
; } ),
360 void RehearseTimingsActivity::viewChanged( const UnoViewSharedPtr
& rView
)
362 // find entry corresponding to modified view
363 ViewsVecT::iterator
aModifiedEntry(
368 ( const ViewsVecT::value_type
& cp
)
369 { return rView
== cp
.first
; } )
372 OSL_ASSERT( aModifiedEntry
!= maViews
.end() );
373 if( aModifiedEntry
== maViews
.end() )
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() )
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
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());
416 sal_Int32 n
= nTimeSecs
/ 3600;
419 buf
.append( OUString::number(n
) + ":" );
420 n
= ((nTimeSecs
% 3600) / 60);
423 buf
.append( OUString::number(n
) + ":" );
424 n
= (nTimeSecs
% 60);
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());
443 blackHole
->SetTextColor( COL_BLACK
);
444 blackHole
->SetFillColor( COL_LIGHTGRAY
);
445 blackHole
->SetLineColor( COL_GRAY
);
449 blackHole
->SetTextColor( COL_BLACK
);
450 blackHole
->SetFillColor( COL_WHITE
);
451 blackHole
->SetLineColor( COL_GRAY
);
453 blackHole
->DrawRect( rect
);
454 blackHole
->GetTextBoundRect( rect
, time
);
456 Point( (maSpriteSizePixel
.getX() - rect
.getOpenWidth()) / 2,
460 metaFile
.WindStart();
462 cppcanvas::RendererSharedPtr
renderer(
463 cppcanvas::VCLFactory::createRenderer(
464 canvas
, metaFile
, cppcanvas::Renderer::Parameters() ) );
465 const bool succ
= renderer
->draw();
470 RehearseTimingsActivity::MouseHandler::MouseHandler( RehearseTimingsActivity
& 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();
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
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)
528 bool RehearseTimingsActivity::MouseHandler::handleMouseDragged(
529 awt::MouseEvent
const & evt
)
531 if( mbMouseStartedInArea
)
532 updatePressedState( isInArea(evt
) );
536 bool RehearseTimingsActivity::MouseHandler::handleMouseMoved(
537 awt::MouseEvent
const & /*evt*/ )
542 } // namespace presentation
544 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */